Auto merge of #77144 - flip1995:clippyup, r=Manishearth

Update Clippy

Bi-weekly Clippy update.

This includes a `Cargo.lock` update (d445493479), so probably needs `rollup=never`.

r? `@Manishearth`
This commit is contained in:
bors 2020-09-25 06:23:55 +00:00
commit fadf025723
111 changed files with 3426 additions and 973 deletions

View File

@ -534,7 +534,7 @@ dependencies = [
"if_chain",
"itertools 0.9.0",
"lazy_static",
"pulldown-cmark 0.7.2",
"pulldown-cmark 0.8.0",
"quine-mc_cluskey",
"quote",
"regex-syntax",

View File

@ -1672,10 +1672,12 @@ Released 2018-09-13
[`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
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
@ -1755,6 +1757,7 @@ Released 2018-09-13
[`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
[`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
@ -1774,6 +1777,7 @@ Released 2018-09-13
[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure

View File

@ -8,7 +8,7 @@ something. We appreciate any sort of contributions, and don't want a wall of rul
Clippy welcomes contributions from everyone. There are many ways to contribute to Clippy and the following document
explains how you can contribute and how to get started. If you have any questions about contributing or need help with
anything, feel free to ask questions on issues or visit the `#clippy` on [Discord].
anything, feel free to ask questions on issues or visit the `#clippy` on [Zulip].
All contributors are expected to follow the [Rust Code of Conduct].
@ -23,7 +23,7 @@ All contributors are expected to follow the [Rust Code of Conduct].
- [Bors and Homu](#bors-and-homu)
- [Contributions](#contributions)
[Discord]: https://discord.gg/rust-lang
[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct
## Getting started
@ -242,7 +242,7 @@ to be run inside the `rust` directory):
```
3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
accelerate the process ping the `@rust-lang/clippy` team in your PR and/or
~~annoy~~ ask them in the [Discord] channel.)
~~annoy~~ ask them in the [Zulip] stream.)
### Syncing back changes in Clippy to [`rust-lang/rust`]

View File

@ -14,7 +14,7 @@ pub fn run(rustc_path: Option<&str>) {
// we can unwrap here because the arg is required here
let rustc_path = PathBuf::from(rustc_path.unwrap());
assert!(rustc_path.is_dir(), "path is not a directory");
let rustc_source_basedir = rustc_path.join("src");
let rustc_source_basedir = rustc_path.join("compiler");
assert!(
rustc_source_basedir.is_dir(),
"are you sure the path leads to a rustc repo?"
@ -61,7 +61,7 @@ fn inject_deps_into_manifest(
let new_deps = extern_crates.map(|dep| {
// format the dependencies that are going to be put inside the Cargo.toml
format!(
"{dep} = {{ path = \"{source_path}/lib{dep}\" }}\n",
"{dep} = {{ path = \"{source_path}/{dep}\" }}\n",
dep = dep,
source_path = rustc_source_dir.display()
)

View File

@ -21,7 +21,7 @@ cargo_metadata = "0.11.1"
if_chain = "1.0.0"
itertools = "0.9"
lazy_static = "1.0.2"
pulldown-cmark = { version = "0.7.1", default-features = false }
pulldown-cmark = { version = "0.8", default-features = false }
quine-mc_cluskey = "0.2.2"
regex-syntax = "0.6"
serde = { version = "1.0", features = ["derive"] }

View File

@ -8,7 +8,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:** Checks for usage of invalid atomic
/// ordering in atomic loads/stores and memory fences.
/// ordering in atomic loads/stores/exchanges/updates and
/// memory fences.
///
/// **Why is this bad?** Using an invalid atomic ordering
/// will cause a panic at run-time.
@ -17,22 +18,35 @@ declare_clippy_lint! {
///
/// **Example:**
/// ```rust,no_run
/// # use std::sync::atomic::{self, AtomicBool, Ordering};
/// # use std::sync::atomic::{self, AtomicU8, Ordering};
///
/// let x = AtomicBool::new(true);
/// let x = AtomicU8::new(0);
///
/// // Bad: `Release` and `AcqRel` cannot be used for `load`.
/// let _ = x.load(Ordering::Release);
/// let _ = x.load(Ordering::AcqRel);
///
/// x.store(false, Ordering::Acquire);
/// x.store(false, Ordering::AcqRel);
/// // Bad: `Acquire` and `AcqRel` cannot be used for `store`.
/// x.store(1, Ordering::Acquire);
/// x.store(2, Ordering::AcqRel);
///
/// // Bad: `Relaxed` cannot be used as a fence's ordering.
/// atomic::fence(Ordering::Relaxed);
/// atomic::compiler_fence(Ordering::Relaxed);
///
/// // Bad: `Release` and `AcqRel` are both always invalid
/// // for the failure ordering (the last arg).
/// let _ = x.compare_exchange(1, 2, Ordering::SeqCst, Ordering::Release);
/// let _ = x.compare_exchange_weak(2, 3, Ordering::AcqRel, Ordering::AcqRel);
///
/// // Bad: The failure ordering is not allowed to be
/// // stronger than the success order, and `SeqCst` is
/// // stronger than `Relaxed`.
/// let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |val| Some(val + val));
/// ```
pub INVALID_ATOMIC_ORDERING,
correctness,
"usage of invalid atomic ordering in atomic loads/stores and memory fences"
"usage of invalid atomic ordering in atomic operations and memory fences"
}
declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]);
@ -127,9 +141,89 @@ fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
}
}
fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<DefId> {
if let ExprKind::Path(ref ord_qpath) = ord_arg.kind {
cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()
} else {
None
}
}
fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind;
let method = method_path.ident.name.as_str();
if type_is_atomic(cx, &args[0]);
if method == "compare_exchange" || method == "compare_exchange_weak" || method == "fetch_update";
let (success_order_arg, failure_order_arg) = if method == "fetch_update" {
(&args[1], &args[2])
} else {
(&args[3], &args[4])
};
if let Some(fail_ordering_def_id) = opt_ordering_defid(cx, failure_order_arg);
then {
// Helper type holding on to some checking and error reporting data. Has
// - (success ordering name,
// - list of failure orderings forbidden by the success order,
// - suggestion message)
type OrdLintInfo = (&'static str, &'static [&'static str], &'static str);
let relaxed: OrdLintInfo = ("Relaxed", &["SeqCst", "Acquire"], "ordering mode `Relaxed`");
let acquire: OrdLintInfo = ("Acquire", &["SeqCst"], "ordering modes `Acquire` or `Relaxed`");
let seq_cst: OrdLintInfo = ("SeqCst", &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`");
let release = ("Release", relaxed.1, relaxed.2);
let acqrel = ("AcqRel", acquire.1, acquire.2);
let search = [relaxed, acquire, seq_cst, release, acqrel];
let success_lint_info = opt_ordering_defid(cx, success_order_arg)
.and_then(|success_ord_def_id| -> Option<OrdLintInfo> {
search
.iter()
.find(|(ordering, ..)| {
match_def_path(cx, success_ord_def_id,
&["core", "sync", "atomic", "Ordering", ordering])
})
.copied()
});
if match_ordering_def_path(cx, fail_ordering_def_id, &["Release", "AcqRel"]) {
// If we don't know the success order is, use what we'd suggest
// if it were maximally permissive.
let suggested = success_lint_info.unwrap_or(seq_cst).2;
span_lint_and_help(
cx,
INVALID_ATOMIC_ORDERING,
failure_order_arg.span,
&format!(
"{}'s failure ordering may not be `Release` or `AcqRel`",
method,
),
None,
&format!("consider using {} instead", suggested),
);
} else if let Some((success_ord_name, bad_ords_given_success, suggested)) = success_lint_info {
if match_ordering_def_path(cx, fail_ordering_def_id, bad_ords_given_success) {
span_lint_and_help(
cx,
INVALID_ATOMIC_ORDERING,
failure_order_arg.span,
&format!(
"{}'s failure ordering may not be stronger than the success ordering of `{}`",
method,
success_ord_name,
),
None,
&format!("consider using {} instead", suggested),
);
}
}
}
}
}
impl<'tcx> LateLintPass<'tcx> for AtomicOrdering {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
check_atomic_load_store(cx, expr);
check_memory_fence(cx, expr);
check_atomic_compare_exchange(cx, expr);
}
}

View File

@ -10,7 +10,7 @@ declare_clippy_lint! {
/// **What it does:** Checks for calls to await while holding a
/// non-async-aware MutexGuard.
///
/// **Why is this bad?** The Mutex types found in syd::sync and parking_lot
/// **Why is this bad?** The Mutex types found in std::sync and parking_lot
/// are not designed to operate in an async context across await points.
///
/// There are two potential solutions. One is to use an asynx-aware Mutex

View File

@ -90,7 +90,7 @@ declare_clippy_lint! {
/// if x & 0b1111 == 0 { }
/// ```
pub VERBOSE_BIT_MASK,
style,
pedantic,
"expressions where a bit mask is less readable than the corresponding method call"
}

View File

@ -1,6 +1,5 @@
use crate::utils::{
contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability,
span_lint_and_sugg, walk_ptrs_ty,
contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability, span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc_ast::ast::UintTy;
@ -53,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind;
if op.node == BinOpKind::Eq;
if match_type(cx,
walk_ptrs_ty(cx.typeck_results().expr_ty(&filter_args[0])),
cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(),
&paths::SLICE_ITER);
then {
let needle = match get_path_name(l) {
@ -63,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
_ => { return; }
}
};
if ty::Uint(UintTy::U8) != *walk_ptrs_ty(cx.typeck_results().expr_ty(needle)).kind() {
if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() {
return;
}
let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) =

View File

@ -21,7 +21,7 @@ pub enum Constant {
/// A `String` (e.g., "abc").
Str(String),
/// A binary string (e.g., `b"abc"`).
Binary(Lrc<Vec<u8>>),
Binary(Lrc<[u8]>),
/// A single `char` (e.g., `'a'`).
Char(char),
/// An integer's bit representation.
@ -155,7 +155,7 @@ pub fn lit_to_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
match *lit {
LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
LitKind::Byte(b) => Constant::Int(u128::from(b)),
LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
LitKind::ByteStr(ref s) => Constant::Binary(Lrc::from(s.as_slice())),
LitKind::Char(c) => Constant::Char(c),
LitKind::Int(n, _) => Constant::Int(n),
LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {

View File

@ -534,7 +534,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
return false;
}
let s = if s.ends_with('s') { &s[..s.len() - 1] } else { s };
let s = s.strip_suffix('s').unwrap_or(s);
s.chars().all(char::is_alphanumeric)
&& s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1

View File

@ -7,7 +7,7 @@ use rustc_span::source_map::Spanned;
use crate::consts::{constant, Constant};
use crate::utils::paths;
use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg, walk_ptrs_ty};
use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg};
declare_clippy_lint! {
/// **What it does:** Checks for calculation of subsecond microseconds or milliseconds
@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for DurationSubsec {
if_chain! {
if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, ref left, ref right) = expr.kind;
if let ExprKind::MethodCall(ref method_path, _ , ref args, _) = left.kind;
if match_type(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])), &paths::DURATION);
if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::DURATION);
if let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right);
then {
let suggested_fn = match (method_path.ident.as_str().as_ref(), divisor) {

View File

@ -1,6 +1,6 @@
use crate::utils::SpanlessEq;
use crate::utils::{get_item_name, higher, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt};
use crate::utils::{snippet_with_applicability, span_lint_and_then, walk_ptrs_ty};
use crate::utils::{snippet_with_applicability, span_lint_and_then};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
@ -106,7 +106,7 @@ fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static
if let ExprKind::AddrOf(BorrowKind::Ref, _, ref key) = params[1].kind;
then {
let map = &params[0];
let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(map));
let obj_ty = cx.typeck_results().expr_ty(map).peel_refs();
return if match_type(cx, obj_ty, &paths::BTREEMAP) {
Some(("BTreeMap", map, key))

View File

@ -1,7 +1,5 @@
use crate::utils::paths::{BEGIN_PANIC, BEGIN_PANIC_FMT, FROM_TRAIT};
use crate::utils::{
is_expn_of, is_type_diagnostic_item, match_def_path, method_chain_args, span_lint_and_then, walk_ptrs_ty,
};
use crate::utils::{is_expn_of, is_type_diagnostic_item, match_def_path, method_chain_args, span_lint_and_then};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
@ -96,7 +94,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h
// check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0]));
let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
{

View File

@ -1,7 +1,7 @@
use crate::utils::paths;
use crate::utils::{
is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet,
span_lint_and_then, walk_ptrs_ty,
span_lint_and_then,
};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
@ -90,7 +90,7 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &
if let PatKind::Tuple(ref pats, None) = arms[0].pat.kind;
if pats.len() == 1;
then {
let ty = walk_ptrs_ty(cx.typeck_results().pat_ty(&pats[0]));
let ty = cx.typeck_results().pat_ty(&pats[0]).peel_refs();
if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) {
return None;
}

View File

@ -88,7 +88,7 @@ declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]
impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Index(ref array, ref index) = &expr.kind {
let ty = cx.typeck_results().expr_ty(array);
let ty = cx.typeck_results().expr_ty(array).peel_refs();
if let Some(range) = higher::range(index) {
// Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
if let ty::Array(_, s) = ty.kind() {

View File

@ -5,7 +5,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
use crate::utils::{
get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help,
trait_ref_of_method, walk_ptrs_ty,
trait_ref_of_method,
};
declare_clippy_lint! {
@ -125,7 +125,7 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) {
// Get the real type of 'self'
let fn_def_id = cx.tcx.hir().local_def_id(item.hir_id);
let self_type = cx.tcx.fn_sig(fn_def_id).input(0);
let self_type = walk_ptrs_ty(self_type.skip_binder());
let self_type = self_type.skip_binder().peel_refs();
// Emit either a warning or an error
if implements_trait(cx, self_type, display_trait_id, &[]) {

View File

@ -1,4 +1,4 @@
use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty};
use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg};
use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
@ -285,7 +285,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
})
}
let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr));
let ty = &cx.typeck_results().expr_ty(expr).peel_refs();
match ty.kind() {
ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| {
cx.tcx

View File

@ -230,7 +230,9 @@ mod macro_use;
mod main_recursion;
mod manual_async_fn;
mod manual_non_exhaustive;
mod manual_strip;
mod map_clone;
mod map_err_ignore;
mod map_identity;
mod map_unit_fn;
mod match_on_vec_items;
@ -269,6 +271,7 @@ mod open_options;
mod option_env_unwrap;
mod option_if_let_else;
mod overflow_check_conditional;
mod panic_in_result_fn;
mod panic_unimplemented;
mod partialeq_ne_impl;
mod path_buf_push_overwrite;
@ -625,7 +628,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&main_recursion::MAIN_RECURSION,
&manual_async_fn::MANUAL_ASYNC_FN,
&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
&manual_strip::MANUAL_STRIP,
&map_clone::MAP_CLONE,
&map_err_ignore::MAP_ERR_IGNORE,
&map_identity::MAP_IDENTITY,
&map_unit_fn::OPTION_MAP_UNIT_FN,
&map_unit_fn::RESULT_MAP_UNIT_FN,
@ -751,6 +756,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&option_env_unwrap::OPTION_ENV_UNWRAP,
&option_if_let_else::OPTION_IF_LET_ELSE,
&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
&panic_in_result_fn::PANIC_IN_RESULT_FN,
&panic_unimplemented::PANIC,
&panic_unimplemented::PANIC_PARAMS,
&panic_unimplemented::TODO,
@ -835,6 +841,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&types::LET_UNIT_VALUE,
&types::LINKEDLIST,
&types::OPTION_OPTION,
&types::RC_BUFFER,
&types::REDUNDANT_ALLOCATION,
&types::TYPE_COMPLEXITY,
&types::UNIT_ARG,
@ -863,6 +870,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&utils::internal_lints::COMPILER_LINT_FUNCTIONS,
&utils::internal_lints::DEFAULT_LINT,
&utils::internal_lints::LINT_WITHOUT_LINT_PASS,
&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
&utils::internal_lints::OUTER_EXPN_EXPN_DATA,
&utils::internal_lints::PRODUCE_ICE,
&vec::USELESS_VEC,
@ -918,6 +926,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
store.register_late_pass(|| box methods::Methods);
store.register_late_pass(|| box map_clone::MapClone);
store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
store.register_late_pass(|| box shadow::Shadow);
store.register_late_pass(|| box types::LetUnitValue);
store.register_late_pass(|| box types::UnitCmp);
@ -1091,6 +1100,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn);
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
single_char_binding_names_threshold,
@ -1105,6 +1116,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box self_assignment::SelfAssignment);
store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
store.register_late_pass(|| box manual_strip::ManualStrip);
store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@ -1135,6 +1148,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC),
LintId::of(&panic_in_result_fn::PANIC_IN_RESULT_FN),
LintId::of(&panic_unimplemented::PANIC),
LintId::of(&panic_unimplemented::TODO),
LintId::of(&panic_unimplemented::UNIMPLEMENTED),
@ -1152,6 +1166,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(&attrs::INLINE_ALWAYS),
LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK),
LintId::of(&bit_mask::VERBOSE_BIT_MASK),
LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
LintId::of(&copies::MATCH_SAME_ARMS),
LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
@ -1180,6 +1195,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP),
LintId::of(&loops::EXPLICIT_ITER_LOOP),
LintId::of(&macro_use::MACRO_USE_IMPORTS),
LintId::of(&map_err_ignore::MAP_ERR_IGNORE),
LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
LintId::of(&matches::MATCH_BOOL),
LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
@ -1230,6 +1246,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
LintId::of(&utils::internal_lints::DEFAULT_LINT),
LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA),
LintId::of(&utils::internal_lints::PRODUCE_ICE),
]);
@ -1249,7 +1266,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&attrs::USELESS_ATTRIBUTE),
LintId::of(&bit_mask::BAD_BIT_MASK),
LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK),
LintId::of(&bit_mask::VERBOSE_BIT_MASK),
LintId::of(&blacklisted_name::BLACKLISTED_NAME),
LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
LintId::of(&booleans::LOGIC_BUG),
@ -1330,6 +1346,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&main_recursion::MAIN_RECURSION),
LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(&manual_strip::MANUAL_STRIP),
LintId::of(&map_clone::MAP_CLONE),
LintId::of(&map_identity::MAP_IDENTITY),
LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
@ -1474,6 +1491,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::CHAR_LIT_AS_U8),
LintId::of(&types::FN_TO_NUMERIC_CAST),
LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
LintId::of(&types::RC_BUFFER),
LintId::of(&types::REDUNDANT_ALLOCATION),
LintId::of(&types::TYPE_COMPLEXITY),
LintId::of(&types::UNIT_ARG),
@ -1507,7 +1525,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
LintId::of(&bit_mask::VERBOSE_BIT_MASK),
LintId::of(&blacklisted_name::BLACKLISTED_NAME),
LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
LintId::of(&collapsible_if::COLLAPSIBLE_IF),
@ -1622,6 +1639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
LintId::of(&loops::MUT_RANGE_BOUND),
LintId::of(&loops::WHILE_LET_LOOP),
LintId::of(&manual_strip::MANUAL_STRIP),
LintId::of(&map_identity::MAP_IDENTITY),
LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
@ -1775,6 +1793,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
LintId::of(&types::BOX_VEC),
LintId::of(&types::RC_BUFFER),
LintId::of(&types::REDUNDANT_ALLOCATION),
LintId::of(&vec::USELESS_VEC),
]);

View File

@ -1114,7 +1114,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&
if let Some(self_expr) = args.get(0);
if let Some(pushed_item) = args.get(1);
// Check that the method being called is push() on a Vec
if match_type(cx, cx.typeck_results().expr_ty(self_expr), &paths::VEC);
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym!(vec_type));
if path.ident.name.as_str() == "push";
then {
return Some((self_expr, pushed_item))
@ -2601,11 +2601,9 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
span,
NEEDLESS_COLLECT_MSG,
|diag| {
let (arg, pred) = if contains_arg.starts_with('&') {
("x", &contains_arg[1..])
} else {
("&x", &*contains_arg)
};
let (arg, pred) = contains_arg
.strip_prefix('&')
.map_or(("&x", &*contains_arg), |s| ("x", s));
diag.span_suggestion(
span,
"replace with",

View File

@ -0,0 +1,245 @@
use crate::consts::{constant, Constant};
use crate::utils::usage::mutated_variables;
use crate::utils::{
eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then,
};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::BinOpKind;
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
use rustc_span::Span;
declare_clippy_lint! {
/// **What it does:**
/// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using
/// the pattern's length.
///
/// **Why is this bad?**
/// Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no
/// slicing which may panic and the compiler does not need to insert this panic code. It is
/// also sometimes more readable as it removes the need for duplicating or storing the pattern
/// used by `str::{starts,ends}_with` and in the slicing.
///
/// **Known problems:**
/// None.
///
/// **Example:**
///
/// ```rust
/// let s = "hello, world!";
/// if s.starts_with("hello, ") {
/// assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
/// }
/// ```
/// Use instead:
/// ```rust
/// let s = "hello, world!";
/// if let Some(end) = s.strip_prefix("hello, ") {
/// assert_eq!(end.to_uppercase(), "WORLD!");
/// }
/// ```
pub MANUAL_STRIP,
complexity,
"suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
}
declare_lint_pass!(ManualStrip => [MANUAL_STRIP]);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum StripKind {
Prefix,
Suffix,
}
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
if let Some((cond, then, _)) = higher::if_block(&expr);
if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind;
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id);
if let ExprKind::Path(target_path) = &target_arg.kind;
then {
let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) {
StripKind::Prefix
} else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) {
StripKind::Suffix
} else {
return;
};
let target_res = qpath_res(cx, &target_path, target_arg.hir_id);
if target_res == Res::Err {
return;
};
if_chain! {
if let Res::Local(hir_id) = target_res;
if let Some(used_mutably) = mutated_variables(then, cx);
if used_mutably.contains(&hir_id);
then {
return;
}
}
let strippings = find_stripping(cx, strip_kind, target_res, pattern, then);
if !strippings.is_empty() {
let kind_word = match strip_kind {
StripKind::Prefix => "prefix",
StripKind::Suffix => "suffix",
};
let test_span = expr.span.until(then.span);
span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {} manually", kind_word), |diag| {
diag.span_note(test_span, &format!("the {} was tested here", kind_word));
multispan_sugg(
diag,
&format!("try using the `strip_{}` method", kind_word),
vec![(test_span,
format!("if let Some(<stripped>) = {}.strip_{}({}) ",
snippet(cx, target_arg.span, ".."),
kind_word,
snippet(cx, pattern.span, "..")))]
.into_iter().chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))),
)
});
}
}
}
}
}
// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise.
fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
if_chain! {
if let ExprKind::MethodCall(_, _, [arg], _) = expr.kind;
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if match_def_path(cx, method_def_id, &paths::STR_LEN);
then {
Some(arg)
} else {
None
}
}
}
// Returns the length of the `expr` if it's a constant string or char.
fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
let (value, _) = constant(cx, cx.typeck_results(), expr)?;
match value {
Constant::Str(value) => Some(value.len() as u128),
Constant::Char(value) => Some(value.len_utf8() as u128),
_ => None,
}
}
// Tests if `expr` equals the length of the pattern.
fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
if let ExprKind::Lit(Spanned {
node: LitKind::Int(n, _),
..
}) = expr.kind
{
constant_length(cx, pattern).map_or(false, |length| length == n)
} else {
len_arg(cx, expr).map_or(false, |arg| eq_expr_value(cx, pattern, arg))
}
}
// Tests if `expr` is a `&str`.
fn is_ref_str(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match cx.typeck_results().expr_ty_adjusted(&expr).kind() {
ty::Ref(_, ty, _) => ty.is_str(),
_ => false,
}
}
// Removes the outer `AddrOf` expression if needed.
fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> {
if let ExprKind::AddrOf(BorrowKind::Ref, _, unref) = &expr.kind {
unref
} else {
expr
}
}
// Find expressions where `target` is stripped using the length of `pattern`.
// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}`
// method.
fn find_stripping<'tcx>(
cx: &LateContext<'tcx>,
strip_kind: StripKind,
target: Res,
pattern: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>,
) -> Vec<Span> {
struct StrippingFinder<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
strip_kind: StripKind,
target: Res,
pattern: &'tcx Expr<'tcx>,
results: Vec<Span>,
}
impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> {
type Map = Map<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
if_chain! {
if is_ref_str(self.cx, ex);
let unref = peel_ref(ex);
if let ExprKind::Index(indexed, index) = &unref.kind;
if let Some(range) = higher::range(index);
if let higher::Range { start, end, .. } = range;
if let ExprKind::Path(path) = &indexed.kind;
if qpath_res(self.cx, path, ex.hir_id) == self.target;
then {
match (self.strip_kind, start, end) {
(StripKind::Prefix, Some(start), None) => {
if eq_pattern_length(self.cx, self.pattern, start) {
self.results.push(ex.span);
return;
}
},
(StripKind::Suffix, None, Some(end)) => {
if_chain! {
if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind;
if let Some(left_arg) = len_arg(self.cx, left);
if let ExprKind::Path(left_path) = &left_arg.kind;
if qpath_res(self.cx, left_path, left_arg.hir_id) == self.target;
if eq_pattern_length(self.cx, self.pattern, right);
then {
self.results.push(ex.span);
return;
}
}
},
_ => {}
}
}
}
walk_expr(self, ex);
}
}
let mut finder = StrippingFinder {
cx,
strip_kind,
target,
pattern,
results: vec![],
};
walk_expr(&mut finder, expr);
finder.results
}

View File

@ -0,0 +1,147 @@
use crate::utils::span_lint_and_help;
use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:** Checks for instances of `map_err(|_| Some::Enum)`
///
/// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to contain and report the cause of the error
///
/// **Known problems:** None.
///
/// **Example:**
/// Before:
/// ```rust
/// use std::fmt;
///
/// #[derive(Debug)]
/// enum Error {
/// Indivisible,
/// Remainder(u8),
/// }
///
/// impl fmt::Display for Error {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// match self {
/// Error::Indivisible => write!(f, "could not divide input by three"),
/// Error::Remainder(remainder) => write!(
/// f,
/// "input is not divisible by three, remainder = {}",
/// remainder
/// ),
/// }
/// }
/// }
///
/// impl std::error::Error for Error {}
///
/// fn divisible_by_3(input: &str) -> Result<(), Error> {
/// input
/// .parse::<i32>()
/// .map_err(|_| Error::Indivisible)
/// .map(|v| v % 3)
/// .and_then(|remainder| {
/// if remainder == 0 {
/// Ok(())
/// } else {
/// Err(Error::Remainder(remainder as u8))
/// }
/// })
/// }
/// ```
///
/// After:
/// ```rust
/// use std::{fmt, num::ParseIntError};
///
/// #[derive(Debug)]
/// enum Error {
/// Indivisible(ParseIntError),
/// Remainder(u8),
/// }
///
/// impl fmt::Display for Error {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// match self {
/// Error::Indivisible(_) => write!(f, "could not divide input by three"),
/// Error::Remainder(remainder) => write!(
/// f,
/// "input is not divisible by three, remainder = {}",
/// remainder
/// ),
/// }
/// }
/// }
///
/// impl std::error::Error for Error {
/// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
/// match self {
/// Error::Indivisible(source) => Some(source),
/// _ => None,
/// }
/// }
/// }
///
/// fn divisible_by_3(input: &str) -> Result<(), Error> {
/// input
/// .parse::<i32>()
/// .map_err(Error::Indivisible)
/// .map(|v| v % 3)
/// .and_then(|remainder| {
/// if remainder == 0 {
/// Ok(())
/// } else {
/// Err(Error::Remainder(remainder as u8))
/// }
/// })
/// }
/// ```
pub MAP_ERR_IGNORE,
pedantic,
"`map_err` should not ignore the original error"
}
declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]);
impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
// do not try to lint if this is from a macro or desugaring
fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
if e.span.from_expansion() {
return;
}
// check if this is a method call (e.g. x.foo())
if let ExprKind::MethodCall(ref method, _t_span, ref args, _) = 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 {
// make sure the first argument is a closure, and grab the CaptureRef, body_id, and body_span fields
if let ExprKind::Closure(capture, _, body_id, body_span, _) = args[1].kind {
// check if this is by Reference (meaning there's no move statement)
if capture == CaptureBy::Ref {
// Get the closure body to check the parameters and values
let closure_body = cx.tcx.hir().body(body_id);
// make sure there's only one parameter (`|_|`)
if closure_body.params.len() == 1 {
// make sure that parameter is the wild token (`_`)
if let PatKind::Wild = closure_body.params[0].pat.kind {
// span the area of the closure capture and warn that the
// original error will be thrown away
span_lint_and_help(
cx,
MAP_ERR_IGNORE,
body_span,
"`map_err(|_|...` ignores the original error",
None,
"Consider wrapping the error in an enum variant",
);
}
}
}
}
}
}
}
}

View File

@ -1,4 +1,3 @@
use crate::utils::walk_ptrs_ty;
use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
@ -90,12 +89,12 @@ fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opti
fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let ty = cx.typeck_results().expr_ty(expr);
let ty = walk_ptrs_ty(ty);
let ty = ty.peel_refs();
is_type_diagnostic_item(cx, ty, sym!(vec_type))
}
fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let ty = cx.typeck_results().expr_ty(expr);
let ty = walk_ptrs_ty(ty);
let ty = ty.peel_refs();
is_type_lang_item(cx, ty, LangItem::RangeFull)
}

View File

@ -6,7 +6,7 @@ use crate::utils::{
expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet,
snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
span_lint_and_then, walk_ptrs_ty,
span_lint_and_then,
};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
@ -502,7 +502,7 @@ impl_lint_pass!(Matches => [
impl<'tcx> LateLintPass<'tcx> for Matches {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if in_external_macro(cx.sess(), expr.span) {
if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
return;
}
@ -794,7 +794,7 @@ fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms
}
fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
let ex_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(ex));
let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) {
for arm in arms {
if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind {

View File

@ -12,6 +12,7 @@ use rustc_middle::hir::map::Map;
use rustc_span::Span;
pub(crate) struct OptionAndThenSome;
impl BindInsteadOfMap for OptionAndThenSome {
const TYPE_NAME: &'static str = "Option";
const TYPE_QPATH: &'static [&'static str] = &paths::OPTION;
@ -24,6 +25,7 @@ impl BindInsteadOfMap for OptionAndThenSome {
}
pub(crate) struct ResultAndThenOk;
impl BindInsteadOfMap for ResultAndThenOk {
const TYPE_NAME: &'static str = "Result";
const TYPE_QPATH: &'static [&'static str] = &paths::RESULT;
@ -36,6 +38,7 @@ impl BindInsteadOfMap for ResultAndThenOk {
}
pub(crate) struct ResultOrElseErrInfo;
impl BindInsteadOfMap for ResultOrElseErrInfo {
const TYPE_NAME: &'static str = "Result";
const TYPE_QPATH: &'static [&'static str] = &paths::RESULT;
@ -120,9 +123,9 @@ pub(crate) trait BindInsteadOfMap {
}
}
fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) {
fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool {
let mut suggs = Vec::new();
let can_sugg = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
if_chain! {
if !in_macro(ret_expr.span);
if let hir::ExprKind::Call(ref func_path, ref args) = ret_expr.kind;
@ -153,12 +156,13 @@ pub(crate) trait BindInsteadOfMap {
)
});
}
can_sugg
}
/// Lint use of `_.and_then(|x| Some(y))` for `Option`s
fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) -> bool {
if !match_type(cx, cx.typeck_results().expr_ty(&args[0]), Self::TYPE_QPATH) {
return;
return false;
}
match args[1].kind {
@ -166,8 +170,10 @@ pub(crate) trait BindInsteadOfMap {
let closure_body = cx.tcx.hir().body(body_id);
let closure_expr = remove_blocks(&closure_body.value);
if !Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) {
Self::lint_closure(cx, expr, closure_expr);
if Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) {
true
} else {
Self::lint_closure(cx, expr, closure_expr)
}
},
// `_.and_then(Some)` case, which is no-op.
@ -181,8 +187,9 @@ pub(crate) trait BindInsteadOfMap {
snippet(cx, args[0].span, "..").into(),
Applicability::MachineApplicable,
);
true
},
_ => {},
_ => false,
}
}
}

View File

@ -25,14 +25,15 @@ use rustc_span::source_map::Span;
use rustc_span::symbol::{sym, SymbolStr};
use crate::consts::{constant, Constant};
use crate::utils::eager_or_lazy::is_lazyness_candidate;
use crate::utils::usage::mutated_variables;
use crate::utils::{
contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro,
is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats,
last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls,
method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability,
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath,
match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty,
single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth,
SpanlessEq,
};
declare_clippy_lint! {
@ -1454,18 +1455,21 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
["unwrap_or_else", "map"] => {
if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) {
unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or");
unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or");
}
},
["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
["and_then", ..] => {
unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "and");
bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]);
bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]);
let biom_option_linted = bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]);
let biom_result_linted = bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]);
if !biom_option_linted && !biom_result_linted {
unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "and");
}
},
["or_else", ..] => {
unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "or");
bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]);
if !bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]) {
unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "or");
}
},
["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]),
@ -1508,9 +1512,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false),
["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true),
["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"),
["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "get_or_insert"),
["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "ok_or"),
["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"),
["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"),
["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"),
_ => {},
}
@ -1714,37 +1718,6 @@ fn lint_or_fun_call<'tcx>(
name: &str,
args: &'tcx [hir::Expr<'_>],
) {
// Searches an expression for method calls or function calls that aren't ctors
struct FunCallFinder<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
found: bool,
}
impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
let call_found = match &expr.kind {
// ignore enum and struct constructors
hir::ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
hir::ExprKind::MethodCall(..) => true,
_ => false,
};
if call_found {
self.found |= true;
}
if !self.found {
intravisit::walk_expr(self, expr);
}
}
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
intravisit::NestedVisitorMap::None
}
}
/// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
fn check_unwrap_or_default(
cx: &LateContext<'_>,
@ -1801,14 +1774,14 @@ fn lint_or_fun_call<'tcx>(
) {
if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind {
if path.ident.as_str() == "len" {
let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0]));
let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
match ty.kind() {
ty::Slice(_) | ty::Array(_, _) => return,
_ => (),
}
if match_type(cx, ty, &paths::VEC) {
if is_type_diagnostic_item(cx, ty, sym!(vec_type)) {
return;
}
}
@ -1825,8 +1798,7 @@ fn lint_or_fun_call<'tcx>(
if_chain! {
if know_types.iter().any(|k| k.2.contains(&name));
let mut finder = FunCallFinder { cx: &cx, found: false };
if { finder.visit_expr(&arg); finder.found };
if is_lazyness_candidate(cx, arg);
if !contains_return(&arg);
let self_ty = cx.typeck_results().expr_ty(self_expr);
@ -1909,7 +1881,7 @@ fn lint_expect_fun_call(
&& (method_name.ident.name == sym!(as_str) || method_name.ident.name == sym!(as_ref))
&& {
let arg_type = cx.typeck_results().expr_ty(&call_args[0]);
let base_type = walk_ptrs_ty(arg_type);
let base_type = arg_type.peel_refs();
*base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type))
}
{
@ -2170,7 +2142,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
}
fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(arg));
let obj_ty = cx.typeck_results().expr_ty(arg).peel_refs();
if let ty::Adt(_, subst) = obj_ty.kind() {
let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) {
@ -2201,7 +2173,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E
let arg = &args[1];
if let Some(arglists) = method_chain_args(arg, &["chars"]) {
let target = &arglists[0][0];
let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(target));
let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
let ref_str = if *self_ty.kind() == ty::Str {
""
} else if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) {
@ -2229,7 +2201,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E
}
fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0]));
let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
if is_type_diagnostic_item(cx, obj_ty, sym!(string_type)) {
lint_string_extend(cx, expr, args);
}
@ -2412,7 +2384,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_
}
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym!(vec_type))
|| matches!(
&walk_ptrs_ty(cx.typeck_results().expr_ty(caller_expr)).kind(),
&cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(),
ty::Array(_, _)
)
{
@ -2615,7 +2587,7 @@ fn derefs_to_slice<'tcx>(
/// lint use of `unwrap()` for `Option`s and `Result`s
fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) {
let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&unwrap_args[0]));
let obj_ty = cx.typeck_results().expr_ty(&unwrap_args[0]).peel_refs();
let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
Some((UNWRAP_USED, "an Option", "None"))
@ -2643,7 +2615,7 @@ fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::E
/// lint use of `expect()` for `Option`s and `Result`s
fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) {
let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&expect_args[0]));
let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs();
let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
Some((EXPECT_USED, "an Option", "None"))
@ -3162,7 +3134,7 @@ fn lint_chars_cmp(
if segment.ident.name == sym!(Some);
then {
let mut applicability = Applicability::MachineApplicable;
let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty_adjusted(&args[0][0]));
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
if *self_ty.kind() != ty::Str {
return false;
@ -3374,7 +3346,7 @@ fn lint_into_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_
INTO_ITER_ON_REF,
method_span,
&format!(
"this `.into_iter()` call is equivalent to `.{}()` and will not move the `{}`",
"this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`",
method_name, kind,
),
"call directly",

View File

@ -1,78 +1,17 @@
use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg};
use if_chain::if_chain;
use crate::utils::{eager_or_lazy, usage};
use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use super::UNNECESSARY_LAZY_EVALUATIONS;
// Return true if the expression is an accessor of any of the arguments
fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool {
params.iter().any(|arg| {
if_chain! {
if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind;
if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind;
if let [p, ..] = path.segments;
then {
ident.name == p.ident.name
} else {
false
}
}
})
}
fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool {
paths.iter().any(|candidate| match_qpath(path, candidate))
}
fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool {
match expr.kind {
// Closures returning literals can be unconditionally simplified
hir::ExprKind::Lit(_) => true,
hir::ExprKind::Index(ref object, ref index) => {
// arguments are not being indexed into
if expr_uses_argument(object, params) {
false
} else {
// arguments are not used as index
!expr_uses_argument(index, params)
}
},
// Reading fields can be simplified if the object is not an argument of the closure
hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params),
// Paths can be simplified if the root is not the argument, this also covers None
hir::ExprKind::Path(_) => !expr_uses_argument(expr, params),
// Calls to Some, Ok, Err can be considered literals if they don't derive an argument
hir::ExprKind::Call(ref func, ref args) => if_chain! {
if variant_calls; // Disable lint when rules conflict with bind_instead_of_map
if let hir::ExprKind::Path(ref path) = func.kind;
if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]);
then {
// Recursively check all arguments
args.iter().all(|arg| can_simplify(arg, params, variant_calls))
} else {
false
}
},
// For anything more complex than the above, a closure is probably the right solution,
// or the case is handled by an other lint
_ => false,
}
}
/// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be
/// replaced with `<fn>(return value of simple closure)`
pub(super) fn lint<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
args: &'tcx [hir::Expr<'_>],
allow_variant_calls: bool,
simplify_using: &str,
) {
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type));
@ -81,10 +20,13 @@ pub(super) fn lint<'tcx>(
if is_option || is_result {
if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {
let body = cx.tcx.hir().body(eid);
let ex = &body.value;
let params = &body.params;
let body_expr = &body.value;
if can_simplify(ex, params, allow_variant_calls) {
if usage::BindingUsageFinder::are_params_used(cx, body) {
return;
}
if eager_or_lazy::is_eagerness_candidate(cx, body_expr) {
let msg = if is_option {
"unnecessary closure used to substitute value for `Option::None`"
} else {
@ -101,7 +43,7 @@ pub(super) fn lint<'tcx>(
"{0}.{1}({2})",
snippet(cx, args[0].span, ".."),
simplify_using,
snippet(cx, ex.span, ".."),
snippet(cx, body_expr.span, ".."),
),
Applicability::MachineApplicable,
);

View File

@ -17,7 +17,7 @@ use crate::utils::sugg::Sugg;
use crate::utils::{
get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats,
last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg,
span_lint_and_then, span_lint_hir_and_then, walk_ptrs_ty, SpanlessEq,
span_lint_and_then, span_lint_hir_and_then, SpanlessEq,
};
declare_clippy_lint! {
@ -99,11 +99,11 @@ declare_clippy_lint! {
/// if y != x {} // where both are floats
///
/// // Good
/// let error = f64::EPSILON; // Use an epsilon for comparison
/// let error_margin = f64::EPSILON; // Use an epsilon for comparison
/// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
/// // let error = std::f64::EPSILON;
/// if (y - 1.23f64).abs() < error { }
/// if (y - x).abs() > error { }
/// // let error_margin = std::f64::EPSILON;
/// if (y - 1.23f64).abs() < error_margin { }
/// if (y - x).abs() > error_margin { }
/// ```
pub FLOAT_CMP,
correctness,
@ -242,10 +242,10 @@ declare_clippy_lint! {
/// if x == ONE { } // where both are floats
///
/// // Good
/// let error = f64::EPSILON; // Use an epsilon for comparison
/// let error_margin = f64::EPSILON; // Use an epsilon for comparison
/// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
/// // let error = std::f64::EPSILON;
/// if (x - ONE).abs() < error { }
/// // let error_margin = std::f64::EPSILON;
/// if (x - ONE).abs() < error_margin { }
/// ```
pub FLOAT_CMP_CONST,
restriction,
@ -411,16 +411,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
if !is_comparing_arrays {
diag.span_suggestion(
expr.span,
"consider comparing them within some error",
"consider comparing them within some margin of error",
format!(
"({}).abs() {} error",
"({}).abs() {} error_margin",
lhs - rhs,
if op == BinOpKind::Eq { '<' } else { '>' }
),
Applicability::HasPlaceholders, // snippet
);
}
diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error`");
diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
});
} else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) {
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
@ -561,7 +561,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
}
fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let value = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind();
let value = &cx.typeck_results().expr_ty(expr).peel_refs().kind();
if let ty::Array(arr_ty, _) = value {
return matches!(arr_ty.kind(), ty::Float(_));
@ -571,7 +571,7 @@ fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
}
fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
matches!(&walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind(), ty::Array(_, _))
matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
}
fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {

View File

@ -377,8 +377,8 @@ impl EarlyLintPass for MiscEarlyLints {
if let PatKind::Ident(_, ident, None) = arg.pat.kind {
let arg_name = ident.to_string();
if arg_name.starts_with('_') {
if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
if let Some(arg_name) = arg_name.strip_prefix('_') {
if let Some(correspondence) = registered_names.get(arg_name) {
span_lint(
cx,
DUPLICATE_UNDERSCORE_ARGUMENT,
@ -386,7 +386,7 @@ impl EarlyLintPass for MiscEarlyLints {
&format!(
"`{}` already exists, having another argument having almost the same \
name makes code comprehension and documentation more difficult",
arg_name[1..].to_owned()
arg_name
),
);
}

View File

@ -1,4 +1,4 @@
use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method, walk_ptrs_ty};
use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut};
@ -12,8 +12,10 @@ declare_clippy_lint! {
/// `BtreeSet` rely on either the hash or the order of keys be unchanging,
/// so having types with interior mutability is a bad idea.
///
/// **Known problems:** We don't currently account for `Rc` or `Arc`, so
/// this may yield false positives.
/// **Known problems:** It's correct to use a struct, that contains interior mutability
/// as a key, when its `Hash` implementation doesn't access any of the interior mutable types.
/// However, this lint is unable to recognize this, so it causes a false positive in theses cases.
/// The `bytes` crate is a great example of this.
///
/// **Example:**
/// ```rust
@ -96,7 +98,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::
// We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased
// generics (because the compiler cannot ensure immutability for unknown types).
fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
let ty = walk_ptrs_ty(ty);
let ty = ty.peel_refs();
if let Adt(def, substs) = ty.kind() {
if [&paths::HASHMAP, &paths::BTREEMAP, &paths::HASHSET, &paths::BTREESET]
.iter()

View File

@ -6,14 +6,16 @@ use std::ptr;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp};
use rustc_infer::traits::specialization_graph;
use rustc_lint::{LateContext, LateLintPass, Lint};
use rustc_middle::ty::adjustment::Adjust;
use rustc_middle::ty::{Ty, TypeFlags};
use rustc_middle::ty::{AssocKind, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{InnerSpan, Span, DUMMY_SP};
use rustc_typeck::hir_ty_to_ty;
use crate::utils::{in_constant, is_copy, qpath_res, span_lint_and_then};
use crate::utils::{in_constant, qpath_res, span_lint_and_then};
use if_chain::if_chain;
declare_clippy_lint! {
/// **What it does:** Checks for declaration of `const` items which is interior
@ -83,11 +85,10 @@ declare_clippy_lint! {
"referencing `const` with interior mutability"
}
#[allow(dead_code)]
#[derive(Copy, Clone)]
enum Source {
Item { item: Span },
Assoc { item: Span, ty: Span },
Assoc { item: Span },
Expr { expr: Span },
}
@ -110,10 +111,15 @@ impl Source {
}
fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) {
if ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) || is_copy(cx, ty) {
// An `UnsafeCell` is `!Copy`, and an `UnsafeCell` is also the only type which
// is `!Freeze`, thus if our type is `Copy` we can be sure it must be `Freeze`
// as well.
// Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`,
// making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is
// 'unfrozen'. However, this code causes a false negative in which
// a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell<T>`.
// Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)`
// since it works when a pointer indirection involves (`Cell<*const T>`).
// Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option;
// but I'm not sure whether it's a decent way, if possible.
if cx.tcx.layout_of(cx.param_env.and(ty)).is_err() || ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) {
return;
}
@ -127,11 +133,7 @@ fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) {
let const_kw_span = span.from_inner(InnerSpan::new(0, 5));
diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)");
},
Source::Assoc { ty: ty_span, .. } => {
if ty.flags().intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) {
diag.span_label(ty_span, &format!("consider requiring `{}` to be `Copy`", ty));
}
},
Source::Assoc { .. } => (),
Source::Expr { .. } => {
diag.help("assign this const to a local or static variable, and use the variable here");
},
@ -152,14 +154,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) {
if let TraitItemKind::Const(hir_ty, ..) = &trait_item.kind {
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
verify_ty_bound(
cx,
ty,
Source::Assoc {
ty: hir_ty.span,
item: trait_item.span,
},
);
// Normalize assoc types because ones originated from generic params
// bounded other traits could have their bound.
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
verify_ty_bound(cx, normalized, Source::Assoc { item: trait_item.span });
}
}
@ -167,17 +165,50 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
if let ImplItemKind::Const(hir_ty, ..) = &impl_item.kind {
let item_hir_id = cx.tcx.hir().get_parent_node(impl_item.hir_id);
let item = cx.tcx.hir().expect_item(item_hir_id);
// Ensure the impl is an inherent impl.
if let ItemKind::Impl { of_trait: None, .. } = item.kind {
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
verify_ty_bound(
cx,
ty,
Source::Assoc {
ty: hir_ty.span,
item: impl_item.span,
},
);
match &item.kind {
ItemKind::Impl {
of_trait: Some(of_trait_ref),
..
} => {
if_chain! {
// Lint a trait impl item only when the definition is a generic type,
// assuming a assoc const is not meant to be a interior mutable type.
if let Some(of_trait_def_id) = of_trait_ref.trait_def_id();
if let Some(of_assoc_item) = specialization_graph::Node::Trait(of_trait_def_id)
.item(cx.tcx, impl_item.ident, AssocKind::Const, of_trait_def_id);
if cx
.tcx
.layout_of(cx.tcx.param_env(of_trait_def_id).and(
// Normalize assoc types because ones originated from generic params
// bounded other traits could have their bound at the trait defs;
// and, in that case, the definition is *not* generic.
cx.tcx.normalize_erasing_regions(
cx.tcx.param_env(of_trait_def_id),
cx.tcx.type_of(of_assoc_item.def_id),
),
))
.is_err();
then {
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
verify_ty_bound(
cx,
normalized,
Source::Assoc {
item: impl_item.span,
},
);
}
}
},
ItemKind::Impl { of_trait: None, .. } => {
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
// Normalize assoc types originated from generic params.
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
verify_ty_bound(cx, normalized, Source::Assoc { item: impl_item.span });
},
_ => (),
}
}
}

View File

@ -1,4 +1,4 @@
use crate::utils::{match_type, paths, span_lint, walk_ptrs_ty};
use crate::utils::{match_type, paths, span_lint};
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
@ -30,7 +30,7 @@ declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]);
impl<'tcx> LateLintPass<'tcx> for OpenOptions {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
if let ExprKind::MethodCall(ref path, _, ref arguments, _) = e.kind {
let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&arguments[0]));
let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
let mut options = Vec::new();
get_open_options(cx, &arguments[0], &mut options);
@ -58,7 +58,7 @@ enum OpenOption {
fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
if let ExprKind::MethodCall(ref path, _, ref arguments, _) = argument.kind {
let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&arguments[0]));
let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
// Only proceed if this is a call on some object of type std::fs::OpenOptions
if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {

View File

@ -1,6 +1,7 @@
use crate::utils;
use crate::utils::eager_or_lazy;
use crate::utils::sugg::Sugg;
use crate::utils::{match_type, paths, span_lint_and_sugg};
use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
@ -13,22 +14,16 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:**
/// Lints usage of `if let Some(v) = ... { y } else { x }` which is more
/// idiomatically done with `Option::map_or` (if the else bit is a simple
/// expression) or `Option::map_or_else` (if the else bit is a longer
/// block).
/// idiomatically done with `Option::map_or` (if the else bit is a pure
/// expression) or `Option::map_or_else` (if the else bit is an impure
/// expresion).
///
/// **Why is this bad?**
/// Using the dedicated functions of the Option type is clearer and
/// more concise than an if let expression.
///
/// **Known problems:**
/// This lint uses whether the block is just an expression or if it has
/// more statements to decide whether to use `Option::map_or` or
/// `Option::map_or_else`. If you have a single expression which calls
/// an expensive function, then it would be more efficient to use
/// `Option::map_or_else`, but this lint would suggest `Option::map_or`.
///
/// Also, this lint uses a deliberately conservative metric for checking
/// This lint uses a deliberately conservative metric for checking
/// if the inside of either body contains breaks or continues which will
/// cause it to not suggest a fix if either block contains a loop with
/// continues or breaks contained within the loop.
@ -73,7 +68,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind {
path.ident.name.to_ident_string() == "ok"
&& match_type(cx, &cx.typeck_results().expr_ty(&receiver), &paths::RESULT)
&& is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym!(result_type))
} else {
false
}
@ -92,6 +87,7 @@ struct OptionIfLetElseOccurence {
struct ReturnBreakContinueMacroVisitor {
seen_return_break_continue: bool,
}
impl ReturnBreakContinueMacroVisitor {
fn new() -> ReturnBreakContinueMacroVisitor {
ReturnBreakContinueMacroVisitor {
@ -99,6 +95,7 @@ impl ReturnBreakContinueMacroVisitor {
}
}
}
impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
type Map = Map<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
@ -157,7 +154,7 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> {
}
/// If this is the else body of an if/else expression, then we need to wrap
/// it in curcly braces. Otherwise, we don't.
/// it in curly braces. Otherwise, we don't.
fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| {
if let Some(Expr {
@ -199,7 +196,10 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
/// If this expression is the option if let/else construct we're detecting, then
/// this function returns an `OptionIfLetElseOccurence` struct with details if
/// this construct is found, or None if this construct is not found.
fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OptionIfLetElseOccurence> {
fn detect_option_if_let_else<'tcx>(
cx: &'_ LateContext<'tcx>,
expr: &'_ Expr<'tcx>,
) -> Option<OptionIfLetElseOccurence> {
if_chain! {
if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly
if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind;
@ -214,10 +214,7 @@ fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Op
let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
let some_body = extract_body_from_arm(&arms[0])?;
let none_body = extract_body_from_arm(&arms[1])?;
let method_sugg = match &none_body.kind {
ExprKind::Block(..) => "map_or_else",
_ => "map_or",
};
let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" };
let capture_name = id.name.to_ident_string();
let wrap_braces = should_wrap_in_braces(cx, expr);
let (as_ref, as_mut) = match &cond_expr.kind {
@ -243,8 +240,8 @@ fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Op
}
}
impl<'a> LateLintPass<'a> for OptionIfLetElse {
fn check_expr(&mut self, cx: &LateContext<'a>, expr: &Expr<'_>) {
impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if let Some(detection) = detect_option_if_let_else(cx, expr) {
span_lint_and_sugg(
cx,

View File

@ -0,0 +1,90 @@
use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then};
use rustc_hir as hir;
use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
declare_clippy_lint! {
/// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result.
///
/// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// fn result_with_panic() -> Result<bool, String>
/// {
/// panic!("error");
/// }
/// ```
pub PANIC_IN_RESULT_FN,
restriction,
"functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` "
}
declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]);
impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
fn_kind: FnKind<'tcx>,
_: &'tcx hir::FnDecl<'tcx>,
body: &'tcx hir::Body<'tcx>,
span: Span,
hir_id: hir::HirId,
) {
if !matches!(fn_kind, FnKind::Closure(_))
&& is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type))
{
lint_impl_body(cx, span, body);
}
}
}
struct FindPanicUnimplementedUnreachable {
result: Vec<Span>,
}
impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable {
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
if ["unimplemented", "unreachable", "panic", "todo"]
.iter()
.any(|fun| is_expn_of(expr.span, fun).is_some())
{
self.result.push(expr.span);
}
// and check sub-expressions
intravisit::walk_expr(self, expr);
}
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
}
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() };
panics.visit_expr(&body.value);
if !panics.result.is_empty() {
span_lint_and_then(
cx,
PANIC_IN_RESULT_FN,
impl_span,
"used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`",
move |diag| {
diag.help(
"`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
);
diag.span_note(panics.result, "return Err() instead of panicking");
},
);
}
}

View File

@ -1,4 +1,4 @@
use crate::utils::{match_type, paths, span_lint_and_sugg, walk_ptrs_ty};
use crate::utils::{match_type, paths, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite {
if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind;
if path.ident.name == sym!(push);
if args.len() == 2;
if match_type(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])), &paths::PATH_BUF);
if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::PATH_BUF);
if let Some(get_index_arg) = args.get(1);
if let ExprKind::Lit(ref lit) = get_index_arg.kind;
if let LitKind::Str(ref path_lit, _) = lit.node;

View File

@ -240,10 +240,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
);
let mut app = Applicability::MaybeIncorrect;
let mut call_snip = &snip[dot + 1..];
let call_snip = &snip[dot + 1..];
// Machine applicable when `call_snip` looks like `foobar()`
if call_snip.ends_with("()") {
call_snip = call_snip[..call_snip.len()-2].trim();
if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) {
if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') {
app = Applicability::MachineApplicable;
}

View File

@ -1,5 +1,5 @@
use crate::consts::{constant_context, Constant};
use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty};
use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&count);
if !in_macro(receiver.span);
then {
let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&receiver));
let ty = cx.typeck_results().expr_ty(&receiver).peel_refs();
if ty.is_str() {
span_lint_and_sugg(
cx,

View File

@ -25,7 +25,6 @@ declare_clippy_lint! {
/// **Example:**
/// ```rust
/// # let x = 1;
///
/// // Bad
/// let x = &x;
///
@ -75,7 +74,9 @@ declare_clippy_lint! {
/// names to bindings or introducing more scopes to contain the bindings.
///
/// **Known problems:** This lint, as the other shadowing related lints,
/// currently only catches very simple patterns.
/// currently only catches very simple patterns. Note that
/// `allow`/`warn`/`deny`/`forbid` attributes only work on the function level
/// for this lint.
///
/// **Example:**
/// ```rust

View File

@ -8,7 +8,7 @@ use rustc_span::source_map::Spanned;
use if_chain::if_chain;
use crate::utils::SpanlessEq;
use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg, walk_ptrs_ty};
use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg};
declare_clippy_lint! {
/// **What it does:** Checks for string appends of the form `x = x + y` (without
@ -134,7 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd {
}
fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
is_type_diagnostic_item(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(e)), sym!(string_type))
is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym!(string_type))
}
fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {

View File

@ -1,7 +1,6 @@
use crate::utils::sugg::Sugg;
use crate::utils::{
differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then,
walk_ptrs_ty,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
@ -194,7 +193,7 @@ fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<
if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind {
if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind {
if eq_expr_value(cx, lhs1, lhs2) {
let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(lhs1));
let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
if matches!(ty.kind(), ty::Slice(_))
|| matches!(ty.kind(), ty::Array(_, _))

View File

@ -215,11 +215,41 @@ declare_clippy_lint! {
"redundant allocation"
}
declare_clippy_lint! {
/// **What it does:** Checks for Rc<T> and Arc<T> when T is a mutable buffer type such as String or Vec
///
/// **Why is this bad?** Expressions such as Rc<String> have no advantage over Rc<str>, since
/// it is larger and involves an extra level of indirection, and doesn't implement Borrow<str>.
///
/// While mutating a buffer type would still be possible with Rc::get_mut(), it only
/// works if there are no additional references yet, which defeats the purpose of
/// enclosing it in a shared ownership type. Instead, additionally wrapping the inner
/// type with an interior mutable container (such as RefCell or Mutex) would normally
/// be used.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// # use std::rc::Rc;
/// fn foo(interned: Rc<String>) { ... }
/// ```
///
/// Better:
///
/// ```rust,ignore
/// fn foo(interned: Rc<str>) { ... }
/// ```
pub RC_BUFFER,
perf,
"shared ownership of a buffer type"
}
pub struct Types {
vec_box_size_threshold: u64,
}
impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION]);
impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER]);
impl<'tcx> LateLintPass<'tcx> for Types {
fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
@ -272,6 +302,19 @@ fn match_type_parameter(cx: &LateContext<'_>, qpath: &QPath<'_>, path: &[&str])
None
}
fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
if match_type_parameter(cx, qpath, &paths::STRING).is_some() {
return Some("str");
}
if match_type_parameter(cx, qpath, &paths::OS_STRING).is_some() {
return Some("std::ffi::OsStr");
}
if match_type_parameter(cx, qpath, &paths::PATH_BUF).is_some() {
return Some("std::path::Path");
}
None
}
fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Span> {
let last = last_path_segment(qpath);
if_chain! {
@ -321,14 +364,15 @@ impl Types {
if let Some(def_id) = res.opt_def_id() {
if Some(def_id) == cx.tcx.lang_items().owned_box() {
if let Some(span) = match_borrows_parameter(cx, qpath) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
REDUNDANT_ALLOCATION,
hir_ty.span,
"usage of `Box<&T>`",
"try",
snippet(cx, span, "..").to_string(),
Applicability::MachineApplicable,
snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
applicability,
);
return; // don't recurse into the type
}
@ -345,14 +389,15 @@ impl Types {
}
} else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
if let Some(span) = match_type_parameter(cx, qpath, &paths::RC) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
REDUNDANT_ALLOCATION,
hir_ty.span,
"usage of `Rc<Rc<T>>`",
"try",
snippet(cx, span, "..").to_string(),
Applicability::MachineApplicable,
snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
applicability,
);
return; // don't recurse into the type
}
@ -368,25 +413,109 @@ impl Types {
GenericArg::Type(ty) => ty.span,
_ => return,
};
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
REDUNDANT_ALLOCATION,
hir_ty.span,
"usage of `Rc<Box<T>>`",
"try",
format!("Rc<{}>", snippet(cx, inner_span, "..")),
format!(
"Rc<{}>",
snippet_with_applicability(cx, inner_span, "..", &mut applicability)
),
applicability,
);
return; // don't recurse into the type
}
if let Some(alternate) = match_buffer_type(cx, qpath) {
span_lint_and_sugg(
cx,
RC_BUFFER,
hir_ty.span,
"usage of `Rc<T>` when T is a buffer type",
"try",
format!("Rc<{}>", alternate),
Applicability::MachineApplicable,
);
return; // don't recurse into the type
}
if match_type_parameter(cx, qpath, &paths::VEC).is_some() {
let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] {
GenericArg::Type(ty) => match &ty.kind {
TyKind::Path(qpath) => qpath,
_ => return,
},
_ => return,
};
let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] {
GenericArg::Type(ty) => ty.span,
_ => return,
};
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
RC_BUFFER,
hir_ty.span,
"usage of `Rc<T>` when T is a buffer type",
"try",
format!(
"Rc<[{}]>",
snippet_with_applicability(cx, inner_span, "..", &mut applicability)
),
Applicability::MachineApplicable,
);
return; // don't recurse into the type
}
if let Some(span) = match_borrows_parameter(cx, qpath) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
REDUNDANT_ALLOCATION,
hir_ty.span,
"usage of `Rc<&T>`",
"try",
snippet(cx, span, "..").to_string(),
snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
applicability,
);
return; // don't recurse into the type
}
} else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
if let Some(alternate) = match_buffer_type(cx, qpath) {
span_lint_and_sugg(
cx,
RC_BUFFER,
hir_ty.span,
"usage of `Arc<T>` when T is a buffer type",
"try",
format!("Arc<{}>", alternate),
Applicability::MachineApplicable,
);
return; // don't recurse into the type
}
if match_type_parameter(cx, qpath, &paths::VEC).is_some() {
let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] {
GenericArg::Type(ty) => match &ty.kind {
TyKind::Path(qpath) => qpath,
_ => return,
},
_ => return,
};
let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] {
GenericArg::Type(ty) => ty.span,
_ => return,
};
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
RC_BUFFER,
hir_ty.span,
"usage of `Arc<T>` when T is a buffer type",
"try",
format!(
"Arc<[{}]>",
snippet_with_applicability(cx, inner_span, "..", &mut applicability)
),
Applicability::MachineApplicable,
);
return; // don't recurse into the type
@ -546,7 +675,6 @@ impl Types {
// details.
return;
}
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
BORROWED_BOX,
@ -556,8 +684,12 @@ impl Types {
format!(
"&{}{}",
ltopt,
&snippet_with_applicability(cx, inner.span, "..", &mut applicability)
&snippet(cx, inner.span, "..")
),
// To make this `MachineApplicable`, at least one needs to check if it isn't a trait item
// because the trait impls of it will break otherwise;
// and there may be other cases that result in invalid code.
// For example, type coercion doesn't work nicely.
Applicability::Unspecified,
);
return; // don't recurse into the type

View File

@ -1,4 +1,4 @@
use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then, walk_ptrs_ty};
use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
@ -81,7 +81,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
// check for `expect`
if let Some(arglists) = method_chain_args(expr, &["expect"]) {
let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0]));
let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
{
@ -91,7 +91,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
// check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0]));
let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
{

View File

@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
cx,
USELESS_CONVERSION,
e.span,
"useless conversion to the same type",
&format!("useless conversion to the same type: `{}`", b),
"consider removing `.into()`",
sugg,
Applicability::MachineApplicable, // snippet
@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
cx,
USELESS_CONVERSION,
e.span,
"useless conversion to the same type",
&format!("useless conversion to the same type: `{}`", b),
"consider removing `.into_iter()`",
sugg,
Applicability::MachineApplicable, // snippet
@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
cx,
USELESS_CONVERSION,
e.span,
"useless conversion to the same type",
&format!("useless conversion to the same type: `{}`", b),
None,
"consider removing `.try_into()`",
);
@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
cx,
USELESS_CONVERSION,
e.span,
"useless conversion to the same type",
&format!("useless conversion to the same type: `{}`", b),
None,
&hint,
);
@ -166,7 +166,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
cx,
USELESS_CONVERSION,
e.span,
"useless conversion to the same type",
&format!("useless conversion to the same type: `{}`", b),
&sugg_msg,
sugg.to_string(),
Applicability::MachineApplicable, // snippet

View File

@ -51,6 +51,8 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
///
/// The `help` message can be optionally attached to a `Span`.
///
/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
///
/// # Example
///
/// ```ignore
@ -87,6 +89,8 @@ pub fn span_lint_and_help<'a, T: LintContext>(
/// The `note` message is presented separately from the main lint message
/// and is attached to a specific span:
///
/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
///
/// # Example
///
/// ```ignore
@ -126,6 +130,7 @@ pub fn span_lint_and_note<'a, T: LintContext>(
/// Like `span_lint` but allows to add notes, help and suggestions using a closure.
///
/// If you need to customize your lint output a lot, use this function.
/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
where
F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
@ -168,6 +173,10 @@ pub fn span_lint_hir_and_then(
/// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x >
/// 2)"`.
///
/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
///
/// # Example
///
/// ```ignore
/// error: This `.fold` can be more succinctly expressed as `.any`
/// --> $DIR/methods.rs:390:13

View File

@ -0,0 +1,128 @@
//! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa.
//!
//! Things to consider:
//! - has the expression side-effects?
//! - is the expression computationally expensive?
//!
//! See lints:
//! - unnecessary-lazy-evaluations
//! - or-fun-call
//! - option-if-let-else
use crate::utils::is_ctor_or_promotable_const_function;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit;
use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
use rustc_hir::{Block, Expr, ExprKind, Path, QPath};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
/// Is the expr pure (is it free from side-effects)?
/// This function is named so to stress that it isn't exhaustive and returns FNs.
fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Lit(..) | ExprKind::Path(..) | ExprKind::Field(..) => true,
ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr),
ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(|expr| identify_some_pure_patterns(expr)),
ExprKind::Struct(_, fields, expr) => {
fields.iter().all(|f| identify_some_pure_patterns(f.expr))
&& expr.map_or(true, |e| identify_some_pure_patterns(e))
},
ExprKind::Call(
&Expr {
kind:
ExprKind::Path(QPath::Resolved(
_,
Path {
res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..),
..
},
)),
..
},
args,
) => args.iter().all(|expr| identify_some_pure_patterns(expr)),
ExprKind::Block(
&Block {
stmts,
expr: Some(expr),
..
},
_,
) => stmts.is_empty() && identify_some_pure_patterns(expr),
ExprKind::Box(..)
| ExprKind::Array(..)
| ExprKind::Call(..)
| ExprKind::MethodCall(..)
| ExprKind::Binary(..)
| ExprKind::Unary(..)
| ExprKind::Cast(..)
| ExprKind::Type(..)
| ExprKind::DropTemps(..)
| ExprKind::Loop(..)
| ExprKind::Match(..)
| ExprKind::Closure(..)
| ExprKind::Block(..)
| ExprKind::Assign(..)
| ExprKind::AssignOp(..)
| ExprKind::Index(..)
| ExprKind::Break(..)
| ExprKind::Continue(..)
| ExprKind::Ret(..)
| ExprKind::InlineAsm(..)
| ExprKind::LlvmInlineAsm(..)
| ExprKind::Repeat(..)
| ExprKind::Yield(..)
| ExprKind::Err => false,
}
}
/// Identify some potentially computationally expensive patterns.
/// This function is named so to stress that its implementation is non-exhaustive.
/// It returns FNs and FPs.
fn identify_some_potentially_expensive_patterns<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
// Searches an expression for method calls or function calls that aren't ctors
struct FunCallFinder<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
found: bool,
}
impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
let call_found = match &expr.kind {
// ignore enum and struct constructors
ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
ExprKind::MethodCall(..) => true,
_ => false,
};
if call_found {
self.found |= true;
}
if !self.found {
intravisit::walk_expr(self, expr);
}
}
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
}
let mut finder = FunCallFinder { cx, found: false };
finder.visit_expr(expr);
finder.found
}
pub fn is_eagerness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
!identify_some_potentially_expensive_patterns(cx, expr) && identify_some_pure_patterns(expr)
}
pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
identify_some_potentially_expensive_patterns(cx, expr)
}

View File

@ -1,6 +1,6 @@
use crate::utils::{
is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint,
span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq,
is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints,
snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
};
use if_chain::if_chain;
use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId};
@ -11,7 +11,7 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::hir_id::CRATE_HIR_ID;
use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Path, StmtKind, Ty, TyKind};
use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
@ -206,6 +206,29 @@ declare_clippy_lint! {
"found collapsible `span_lint_and_then` calls"
}
declare_clippy_lint! {
/// **What it does:** Checks for calls to `utils::match_type()` on a type diagnostic item
/// and suggests to use `utils::is_type_diagnostic_item()` instead.
///
/// **Why is this bad?** `utils::is_type_diagnostic_item()` does not require hardcoded paths.
///
/// **Known problems:** None.
///
/// **Example:**
/// Bad:
/// ```rust,ignore
/// utils::match_type(cx, ty, &paths::VEC)
/// ```
///
/// Good:
/// ```rust,ignore
/// utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))
/// ```
pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
internal,
"using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
}
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
impl EarlyLintPass for ClippyLintsInternal {
@ -404,7 +427,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind;
let fn_name = path.ident;
if let Some(sugg) = self.map.get(&*fn_name.as_str());
let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0]));
let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
if match_type(cx, ty, &paths::EARLY_CONTEXT)
|| match_type(cx, ty, &paths::LATE_CONTEXT);
then {
@ -437,7 +460,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
let args = arg_lists[1];
if args.len() == 1;
let self_arg = &args[0];
let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(self_arg));
let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
then {
span_lint_and_sugg(
@ -652,3 +675,89 @@ fn suggest_note(
Applicability::MachineApplicable,
);
}
declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if !run_lints(cx, &[MATCH_TYPE_ON_DIAGNOSTIC_ITEM], expr.hir_id) {
return;
}
if_chain! {
// Check if this is a call to utils::match_type()
if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
if let ExprKind::Path(fn_qpath) = &fn_path.kind;
if match_qpath(&fn_qpath, &["utils", "match_type"]);
// Extract the path to the matched type
if let Some(segments) = path_to_matched_type(cx, ty_path);
let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
if let Some(ty_did) = path_to_res(cx, &segments[..]).and_then(|res| res.opt_def_id());
// Check if the matched type is a diagnostic item
let diag_items = cx.tcx.diagnostic_items(ty_did.krate);
if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None });
then {
let cx_snippet = snippet(cx, context.span, "_");
let ty_snippet = snippet(cx, ty.span, "_");
span_lint_and_sugg(
cx,
MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
expr.span,
"usage of `utils::match_type()` on a type diagnostic item",
"try",
format!("utils::is_type_diagnostic_item({}, {}, sym!({}))", cx_snippet, ty_snippet, item_name),
Applicability::MaybeIncorrect,
);
}
}
}
}
fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<SymbolStr>> {
use rustc_hir::ItemKind;
match &expr.kind {
ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
ExprKind::Path(qpath) => match qpath_res(cx, qpath, expr.hir_id) {
Res::Local(hir_id) => {
let parent_id = cx.tcx.hir().get_parent_node(hir_id);
if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
if let Some(init) = local.init {
return path_to_matched_type(cx, init);
}
}
},
Res::Def(DefKind::Const | DefKind::Static, def_id) => {
if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
let body = cx.tcx.hir().body(body_id);
return path_to_matched_type(cx, &body.value);
}
}
},
_ => {},
},
ExprKind::Array(exprs) => {
let segments: Vec<SymbolStr> = exprs
.iter()
.filter_map(|expr| {
if let ExprKind::Lit(lit) = &expr.kind {
if let LitKind::Str(sym, _) = lit.node {
return Some(sym.as_str());
}
}
None
})
.collect();
if segments.len() == exprs.len() {
return Some(segments);
}
},
_ => {},
}
None
}

View File

@ -10,6 +10,7 @@ pub mod comparisons;
pub mod conf;
pub mod constants;
mod diagnostics;
pub mod eager_or_lazy;
pub mod higher;
mod hir_utils;
pub mod inspector;
@ -130,6 +131,9 @@ pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
}
/// Checks if type is struct, enum or union type with the given def path.
///
/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
match ty.kind() {
ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
@ -138,6 +142,8 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
}
/// Checks if the type is equal to a diagnostic item
///
/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
match ty.kind() {
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
@ -748,14 +754,6 @@ pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
}
}
/// Returns the base type for references and raw pointers.
pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> {
match ty.kind() {
ty::Ref(_, ty, _) => walk_ptrs_ty(ty),
_ => ty,
}
}
/// Returns the base type for references and raw pointers, and count reference
/// depth.
pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {

View File

@ -113,8 +113,12 @@ pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"];
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
pub const STRING: [&str; 3] = ["alloc", "string", "String"];
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];

View File

@ -49,7 +49,7 @@ impl<'a> Sugg<'a> {
/// Convenience function around `hir_opt` for suggestions with a default
/// text.
pub fn hir(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
Self::hir_opt(cx, expr).unwrap_or_else(|| Sugg::NonParen(Cow::Borrowed(default)))
Self::hir_opt(cx, expr).unwrap_or(Sugg::NonParen(Cow::Borrowed(default)))
}
/// Same as `hir`, but it adapts the applicability level by following rules:

View File

@ -1,6 +1,8 @@
use crate::utils::match_var;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::intravisit;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Expr, HirId, Path};
use rustc_infer::infer::TyCtxtInferExt;
@ -108,3 +110,67 @@ pub fn is_unused<'tcx>(ident: &'tcx Ident, body: &'tcx Expr<'_>) -> bool {
walk_expr(&mut visitor, body);
!visitor.used
}
pub struct ParamBindingIdCollector {
binding_hir_ids: Vec<hir::HirId>,
}
impl<'tcx> ParamBindingIdCollector {
fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
let mut finder = ParamBindingIdCollector {
binding_hir_ids: Vec::new(),
};
finder.visit_body(body);
finder.binding_hir_ids
}
}
impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector {
type Map = Map<'tcx>;
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
if let hir::PatKind::Binding(_, hir_id, ..) = param.pat.kind {
self.binding_hir_ids.push(hir_id);
}
}
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
intravisit::NestedVisitorMap::None
}
}
pub struct BindingUsageFinder<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
binding_ids: Vec<hir::HirId>,
usage_found: bool,
}
impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> {
pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool {
let mut finder = BindingUsageFinder {
cx,
binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body),
usage_found: false,
};
finder.visit_body(body);
finder.usage_found
}
}
impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
if !self.usage_found {
intravisit::walk_expr(self, expr);
}
}
fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
if let hir::def::Res::Local(id) = path.res {
if self.binding_ids.contains(&id) {
self.usage_found = true;
}
}
}
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
}
}

View File

@ -322,11 +322,15 @@ impl EarlyLintPass for Write {
}
/// Given a format string that ends in a newline and its span, calculates the span of the
/// newline.
/// newline, or the format string itself if the format string consists solely of a newline.
fn newline_span(fmtstr: &StrLit) -> Span {
let sp = fmtstr.span;
let contents = &fmtstr.symbol.as_str();
if *contents == r"\n" {
return sp;
}
let newline_sp_hi = sp.hi()
- match fmtstr.style {
StrStyle::Cooked => BytePos(1),

View File

@ -488,7 +488,7 @@ For `LateLintPass` lints:
While most of Clippy's lint utils are documented, most of rustc's internals lack
documentation currently. This is unfortunate, but in most cases you can probably
get away with copying things from existing similar lints. If you are stuck,
don't hesitate to ask on [Discord] or in the issue/PR.
don't hesitate to ask on [Zulip] or in the issue/PR.
[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/mod.rs
[if_chain]: https://docs.rs/if_chain/*/if_chain/
@ -500,4 +500,4 @@ don't hesitate to ask on [Discord] or in the issue/PR.
[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/
[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html
[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html
[Discord]: https://discord.gg/rust-lang
[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy

View File

@ -60,7 +60,7 @@ impl LateLintPass<'_> for MyStructLint {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if_chain! {
// Check our expr is calling a method
if let hir::ExprKind::MethodCall(path, _, _args) = &expr.kind;
if let hir::ExprKind::MethodCall(path, _, _args, _) = &expr.kind;
// Check the name of this method is `some_method`
if path.ident.name == sym!(some_method);
then {

View File

@ -930,7 +930,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
Lint {
name: "invalid_atomic_ordering",
group: "correctness",
desc: "usage of invalid atomic ordering in atomic loads/stores and memory fences",
desc: "usage of invalid atomic ordering in atomic operations and memory fences",
deprecation: None,
module: "atomic_ordering",
},
@ -1144,6 +1144,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "methods",
},
Lint {
name: "manual_strip",
group: "complexity",
desc: "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing",
deprecation: None,
module: "manual_strip",
},
Lint {
name: "manual_swap",
group: "complexity",
@ -1172,6 +1179,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "entry",
},
Lint {
name: "map_err_ignore",
group: "pedantic",
desc: "`map_err` should not ignore the original error",
deprecation: None,
module: "map_err_ignore",
},
Lint {
name: "map_flatten",
group: "pedantic",
@ -1718,6 +1732,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "panic_unimplemented",
},
Lint {
name: "panic_in_result_fn",
group: "restriction",
desc: "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` ",
deprecation: None,
module: "panic_in_result_fn",
},
Lint {
name: "panic_params",
group: "style",
@ -1844,6 +1865,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "ranges",
},
Lint {
name: "rc_buffer",
group: "perf",
desc: "shared ownership of a buffer type",
deprecation: None,
module: "types",
},
Lint {
name: "redundant_allocation",
group: "perf",
@ -2630,7 +2658,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
},
Lint {
name: "verbose_bit_mask",
group: "style",
group: "pedantic",
desc: "expressions where a bit mask is less readable than the corresponding method call",
deprecation: None,
module: "bit_mask",

View File

@ -0,0 +1,45 @@
#![warn(clippy::invalid_atomic_ordering)]
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
// `compare_exchange` (not weak) testing
let x = AtomicUsize::new(0);
// Allowed ordering combos
let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Relaxed);
let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Acquire);
let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Relaxed);
let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Relaxed);
let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Acquire);
let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Relaxed);
let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Relaxed);
let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Acquire);
let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::SeqCst);
// AcqRel is always forbidden as a failure ordering
let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel);
let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel);
let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel);
let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel);
let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel);
// Release is always forbidden as a failure ordering
let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release);
let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release);
let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release);
let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release);
let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release);
// Release success order forbids failure order of Acquire or SeqCst
let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire);
let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst);
// Relaxed success order also forbids failure order of Acquire or SeqCst
let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst);
let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire);
// Acquire/AcqRel forbids failure order of SeqCst
let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst);
let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst);
}

View File

@ -0,0 +1,131 @@
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange.rs:21:57
|
LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel);
| ^^^^^^^^^^^^^^^^
|
= note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange.rs:22:57
|
LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange.rs:23:57
|
LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange.rs:24:56
|
LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange.rs:25:56
|
LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange.rs:28:57
|
LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange.rs:29:57
|
LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange.rs:30:57
|
LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange.rs:31:56
|
LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange.rs:32:56
|
LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release`
--> $DIR/atomic_ordering_exchange.rs:35:57
|
LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release`
--> $DIR/atomic_ordering_exchange.rs:36:57
|
LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed`
--> $DIR/atomic_ordering_exchange.rs:39:57
|
LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed`
--> $DIR/atomic_ordering_exchange.rs:40:57
|
LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange's failure ordering may not be stronger than the success ordering of `Acquire`
--> $DIR/atomic_ordering_exchange.rs:43:57
|
LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: compare_exchange's failure ordering may not be stronger than the success ordering of `AcqRel`
--> $DIR/atomic_ordering_exchange.rs:44:56
|
LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: aborting due to 16 previous errors

View File

@ -0,0 +1,47 @@
#![warn(clippy::invalid_atomic_ordering)]
use std::sync::atomic::{AtomicPtr, Ordering};
fn main() {
let ptr = &mut 5;
let ptr2 = &mut 10;
// `compare_exchange_weak` testing
let x = AtomicPtr::new(ptr);
// Allowed ordering combos
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Relaxed);
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Acquire);
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Relaxed);
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Relaxed);
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Acquire);
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Relaxed);
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Relaxed);
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Acquire);
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::SeqCst);
// AcqRel is always forbidden as a failure ordering
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel);
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel);
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel);
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel);
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel);
// Release is always forbidden as a failure ordering
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release);
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release);
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release);
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release);
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release);
// Release success order forbids failure order of Acquire or SeqCst
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire);
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst);
// Relaxed success order also forbids failure order of Acquire or SeqCst
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst);
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire);
// Acquire/AcqRel forbids failure order of SeqCst
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst);
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst);
}

View File

@ -0,0 +1,131 @@
error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange_weak.rs:23:67
|
LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel);
| ^^^^^^^^^^^^^^^^
|
= note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange_weak.rs:24:67
|
LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange_weak.rs:25:67
|
LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange_weak.rs:26:66
|
LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange_weak.rs:27:66
|
LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange_weak.rs:30:67
|
LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange_weak.rs:31:67
|
LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange_weak.rs:32:67
|
LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange_weak.rs:33:66
|
LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_exchange_weak.rs:34:66
|
LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release`
--> $DIR/atomic_ordering_exchange_weak.rs:37:67
|
LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release`
--> $DIR/atomic_ordering_exchange_weak.rs:38:67
|
LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed`
--> $DIR/atomic_ordering_exchange_weak.rs:41:67
|
LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed`
--> $DIR/atomic_ordering_exchange_weak.rs:42:67
|
LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire);
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Acquire`
--> $DIR/atomic_ordering_exchange_weak.rs:45:67
|
LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `AcqRel`
--> $DIR/atomic_ordering_exchange_weak.rs:46:66
|
LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst);
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: aborting due to 16 previous errors

View File

@ -0,0 +1,45 @@
#![warn(clippy::invalid_atomic_ordering)]
use std::sync::atomic::{AtomicIsize, Ordering};
fn main() {
// `fetch_update` testing
let x = AtomicIsize::new(0);
// Allowed ordering combos
let _ = x.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::Acquire, Ordering::Acquire, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::Acquire, Ordering::Relaxed, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::Release, Ordering::Relaxed, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::AcqRel, Ordering::Acquire, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::AcqRel, Ordering::Relaxed, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::SeqCst, Ordering::Relaxed, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::SeqCst, Ordering::Acquire, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |old| Some(old + 1));
// AcqRel is always forbidden as a failure ordering
let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1));
// Release is always forbidden as a failure ordering
let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1));
// Release success order forbids failure order of Acquire or SeqCst
let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1));
// Relaxed success order also forbids failure order of Acquire or SeqCst
let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1));
// Acquire/AcqRel forbids failure order of SeqCst
let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1));
let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1));
}

View File

@ -0,0 +1,131 @@
error: fetch_update's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_fetch_update.rs:21:47
|
LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^
|
= note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
= help: consider using ordering mode `Relaxed` instead
error: fetch_update's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_fetch_update.rs:22:47
|
LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: fetch_update's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_fetch_update.rs:23:47
|
LL | let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: fetch_update's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_fetch_update.rs:24:46
|
LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: fetch_update's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_fetch_update.rs:25:46
|
LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
error: fetch_update's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_fetch_update.rs:28:47
|
LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: fetch_update's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_fetch_update.rs:29:47
|
LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: fetch_update's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_fetch_update.rs:30:47
|
LL | let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: fetch_update's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_fetch_update.rs:31:46
|
LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: fetch_update's failure ordering may not be `Release` or `AcqRel`
--> $DIR/atomic_ordering_fetch_update.rs:32:46
|
LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
error: fetch_update's failure ordering may not be stronger than the success ordering of `Release`
--> $DIR/atomic_ordering_fetch_update.rs:35:47
|
LL | let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: fetch_update's failure ordering may not be stronger than the success ordering of `Release`
--> $DIR/atomic_ordering_fetch_update.rs:36:47
|
LL | let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed`
--> $DIR/atomic_ordering_fetch_update.rs:39:47
|
LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed`
--> $DIR/atomic_ordering_fetch_update.rs:40:47
|
LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^^
|
= help: consider using ordering mode `Relaxed` instead
error: fetch_update's failure ordering may not be stronger than the success ordering of `Acquire`
--> $DIR/atomic_ordering_fetch_update.rs:43:47
|
LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: fetch_update's failure ordering may not be stronger than the success ordering of `AcqRel`
--> $DIR/atomic_ordering_fetch_update.rs:44:46
|
LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1));
| ^^^^^^^^^^^^^^^^
|
= help: consider using ordering modes `Acquire` or `Relaxed` instead
error: aborting due to 16 previous errors

View File

@ -19,16 +19,30 @@ const NO_ANN: &dyn Display = &70;
static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
const ONCE_INIT: Once = Once::new();
trait Trait<T>: Copy {
type NonCopyType;
trait Trait<T> {
type AssocType;
const ATOMIC: AtomicUsize;
const INPUT: T;
const ASSOC: Self::AssocType;
fn function() {
let _ = &Self::INPUT;
let _ = &Self::ASSOC;
}
}
impl Trait<u32> for u64 {
type NonCopyType = u16;
type AssocType = AtomicUsize;
const ATOMIC: AtomicUsize = AtomicUsize::new(9);
const INPUT: u32 = 10;
const ASSOC: Self::AssocType = AtomicUsize::new(11);
fn function() {
let _ = &Self::INPUT;
let _ = &Self::ASSOC; //~ ERROR interior mutability
}
}
// This is just a pointer that can be safely dereferended,

View File

@ -1,14 +1,22 @@
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:66:5
--> $DIR/borrow_interior_mutable_const.rs:44:18
|
LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
| ^^^^^^
LL | let _ = &Self::ASSOC; //~ ERROR interior mutability
| ^^^^^^^^^^^
|
= note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings`
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:67:16
--> $DIR/borrow_interior_mutable_const.rs:80:5
|
LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
| ^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:81:16
|
LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
| ^^^^^^
@ -16,7 +24,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:70:22
--> $DIR/borrow_interior_mutable_const.rs:84:22
|
LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
@ -24,7 +32,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:71:25
--> $DIR/borrow_interior_mutable_const.rs:85:25
|
LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
@ -32,7 +40,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:72:27
--> $DIR/borrow_interior_mutable_const.rs:86:27
|
LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
@ -40,7 +48,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:73:26
--> $DIR/borrow_interior_mutable_const.rs:87:26
|
LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
@ -48,7 +56,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:84:14
--> $DIR/borrow_interior_mutable_const.rs:98:14
|
LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
| ^^^^^^^^^^^^
@ -56,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:85:14
--> $DIR/borrow_interior_mutable_const.rs:99:14
|
LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
| ^^^^^^^^^^^^
@ -64,7 +72,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:86:19
--> $DIR/borrow_interior_mutable_const.rs:100:19
|
LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
| ^^^^^^^^^^^^
@ -72,7 +80,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:87:14
--> $DIR/borrow_interior_mutable_const.rs:101:14
|
LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
| ^^^^^^^^^^^^
@ -80,7 +88,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:88:13
--> $DIR/borrow_interior_mutable_const.rs:102:13
|
LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
| ^^^^^^^^^^^^
@ -88,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:94:13
--> $DIR/borrow_interior_mutable_const.rs:108:13
|
LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
| ^^^^^^^^^^^^
@ -96,7 +104,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:99:5
--> $DIR/borrow_interior_mutable_const.rs:113:5
|
LL | CELL.set(2); //~ ERROR interior mutability
| ^^^^
@ -104,7 +112,7 @@ LL | CELL.set(2); //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:100:16
--> $DIR/borrow_interior_mutable_const.rs:114:16
|
LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
| ^^^^
@ -112,7 +120,7 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:113:5
--> $DIR/borrow_interior_mutable_const.rs:127:5
|
LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
| ^^^^^^^^^^^
@ -120,12 +128,12 @@ LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:114:16
--> $DIR/borrow_interior_mutable_const.rs:128:16
|
LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
| ^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
error: aborting due to 16 previous errors
error: aborting due to 17 previous errors

View File

@ -1,22 +0,0 @@
#![allow(dead_code)]
enum Foo {
A,
B,
C,
}
macro_rules! test_hash {
($foo:expr, $($t:ident => $ord:expr),+ ) => {
use self::Foo::*;
match $foo {
$ ( & $t => $ord,
)*
};
};
}
fn main() {
let a = Foo::A;
test_hash!(&a, A => 0, B => 1, C => 2);
}

View File

@ -1,17 +0,0 @@
error: you don't need to add `&` to both the expression and the patterns
--> $DIR/ice-2636.rs:12:9
|
LL | / match $foo {
LL | | $ ( & $t => $ord,
LL | | )*
LL | | };
| |_________^
...
LL | test_hash!(&a, A => 0, B => 1, C => 2);
| --------------------------------------- in this macro invocation
|
= note: `-D clippy::match-ref-pats` implied by `-D warnings`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error

View File

@ -34,60 +34,141 @@ static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
#[allow(clippy::declare_interior_mutable_const)]
const ONCE_INIT: Once = Once::new();
trait Trait<T>: Copy {
type NonCopyType;
// a constant whose type is a concrete type should be linted at the definition site.
trait ConcreteTypes {
const ATOMIC: AtomicUsize; //~ ERROR interior mutable
const INTEGER: u64;
const STRING: String;
const SELF: Self; // (no error)
const INPUT: T;
//~^ ERROR interior mutable
//~| HELP consider requiring `T` to be `Copy`
const ASSOC: Self::NonCopyType;
//~^ ERROR interior mutable
//~| HELP consider requiring `<Self as Trait<T>>::NonCopyType` to be `Copy`
const AN_INPUT: T = Self::INPUT;
//~^ ERROR interior mutable
//~| ERROR consider requiring `T` to be `Copy`
declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable
declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable
}
trait Trait2 {
type CopyType: Copy;
const SELF_2: Self;
//~^ ERROR interior mutable
//~| HELP consider requiring `Self` to be `Copy`
const ASSOC_2: Self::CopyType; // (no error)
}
// we don't lint impl of traits, because an impl has no power to change the interface.
impl Trait<u32> for u64 {
type NonCopyType = u16;
impl ConcreteTypes for u64 {
const ATOMIC: AtomicUsize = AtomicUsize::new(9);
const INTEGER: u64 = 10;
const STRING: String = String::new();
const SELF: Self = 11;
const INPUT: u32 = 12;
const ASSOC: Self::NonCopyType = 13;
}
struct Local<T, U>(T, U);
// a helper trait used below
trait ConstDefault {
const DEFAULT: Self;
}
impl<T: Trait2 + Trait<u32>, U: Trait2> Local<T, U> {
const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable
// a constant whose type is a generic type should be linted at the implementation site.
trait GenericTypes<T, U> {
const TO_REMAIN_GENERIC: T;
const TO_BE_CONCRETE: U;
const HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC;
declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC);
}
impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for u64 {
const TO_REMAIN_GENERIC: T = T::DEFAULT;
const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable
}
// a helper type used below
struct Wrapper<T>(T);
// a constant whose type is an associated type should be linted at the implementation site, too.
trait AssocTypes {
type ToBeFrozen;
type ToBeUnfrozen;
type ToBeGenericParam;
const TO_BE_FROZEN: Self::ToBeFrozen;
const TO_BE_UNFROZEN: Self::ToBeUnfrozen;
const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen>;
// to ensure it can handle things when a generic type remains after normalization.
const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam>;
}
impl<T: ConstDefault> AssocTypes for Vec<T> {
type ToBeFrozen = u16;
type ToBeUnfrozen = AtomicUsize;
type ToBeGenericParam = T;
const TO_BE_FROZEN: Self::ToBeFrozen = 12;
const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable
const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable
const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam> = Wrapper(T::DEFAULT);
}
// a helper trait used below
trait AssocTypesHelper {
type NotToBeBounded;
type ToBeBounded;
const NOT_TO_BE_BOUNDED: Self::NotToBeBounded;
}
// a constant whose type is an assoc type originated from a generic param bounded at the definition
// site should be linted at there.
trait AssocTypesFromGenericParam<T>
where
T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
{
const NOT_BOUNDED: T::NotToBeBounded;
const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable
}
impl<T> AssocTypesFromGenericParam<T> for u64
where
T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
{
// an associated type could remain unknown in a trait impl.
const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
const BOUNDED: T::ToBeBounded = AtomicUsize::new(15);
}
// a constant whose type is `Self` should be linted at the implementation site as well.
// (`Option` requires `Sized` bound.)
trait SelfType: Sized {
const SELF: Self;
// this was the one in the original issue (#5050).
const WRAPPED_SELF: Option<Self>;
}
impl SelfType for u64 {
const SELF: Self = 16;
const WRAPPED_SELF: Option<Self> = Some(20);
}
impl SelfType for AtomicUsize {
// this (interior mutable `Self` const) exists in `parking_lot`.
// `const_trait_impl` will replace it in the future, hopefully.
const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable
const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); //~ ERROR interior mutable
}
// Even though a constant contains a generic type, if it also have a interior mutable type,
// it should be linted at the definition site.
trait BothOfCellAndGeneric<T> {
// this is a false negative in the current implementation.
const DIRECT: Cell<T>;
const INDIRECT: Cell<*const T>; //~ ERROR interior mutable
}
impl<T: ConstDefault> BothOfCellAndGeneric<T> for u64 {
const DIRECT: Cell<T> = Cell::new(T::DEFAULT);
const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null());
}
struct Local<T>(T);
// a constant in an inherent impl are essentially the same as a normal const item
// except there can be a generic or associated type.
impl<T> Local<T>
where
T: ConstDefault + AssocTypesHelper<ToBeBounded = AtomicUsize>,
{
const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable
const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy");
const T_SELF: T = T::SELF_2;
const U_SELF: U = U::SELF_2;
//~^ ERROR interior mutable
//~| HELP consider requiring `U` to be `Copy`
const T_ASSOC: T::NonCopyType = T::ASSOC;
//~^ ERROR interior mutable
//~| HELP consider requiring `<T as Trait<u32>>::NonCopyType` to be `Copy`
const U_ASSOC: U::CopyType = U::ASSOC_2;
const GENERIC_TYPE: T = T::DEFAULT;
const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable
}
fn main() {}

View File

@ -36,75 +36,75 @@ LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:40:5
--> $DIR/declare_interior_mutable_const.rs:39:5
|
LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:44:5
|
LL | const INPUT: T;
| ^^^^^^^^^^^^^-^
| |
| consider requiring `T` to be `Copy`
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:47:5
|
LL | const ASSOC: Self::NonCopyType;
| ^^^^^^^^^^^^^-----------------^
| |
| consider requiring `<Self as Trait<T>>::NonCopyType` to be `Copy`
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:51:5
|
LL | const AN_INPUT: T = Self::INPUT;
| ^^^^^^^^^^^^^^^^-^^^^^^^^^^^^^^^
| |
| consider requiring `T` to be `Copy`
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:16:9
|
LL | const $name: $ty = $e;
| ^^^^^^^^^^^^^^^^^^^^^^
...
LL | declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable
| ----------------------------------------------- in this macro invocation
LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable
| ----------------------------------------------------------- in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:60:5
--> $DIR/declare_interior_mutable_const.rs:67:5
|
LL | const SELF_2: Self;
| ^^^^^^^^^^^^^^----^
| |
| consider requiring `Self` to be `Copy`
LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:81:5
--> $DIR/declare_interior_mutable_const.rs:92:5
|
LL | const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:84:5
--> $DIR/declare_interior_mutable_const.rs:93:5
|
LL | const U_SELF: U = U::SELF_2;
| ^^^^^^^^^^^^^^-^^^^^^^^^^^^^
| |
| consider requiring `U` to be `Copy`
LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:87:5
--> $DIR/declare_interior_mutable_const.rs:112:5
|
LL | const T_ASSOC: T::NonCopyType = T::ASSOC;
| ^^^^^^^^^^^^^^^--------------^^^^^^^^^^^^
| |
| consider requiring `<T as Trait<u32>>::NonCopyType` to be `Copy`
LL | const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 13 previous errors
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:140:5
|
LL | const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:141:5
|
LL | const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); //~ ERROR interior mutable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:149:5
|
LL | const INDIRECT: Cell<*const T>; //~ ERROR interior mutable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:165:5
|
LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> $DIR/declare_interior_mutable_const.rs:171:5
|
LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 15 previous errors

View File

@ -1,5 +1,6 @@
#![warn(clippy::drop_ref)]
#![allow(clippy::toplevel_ref_arg)]
#![allow(clippy::map_err_ignore)]
use std::mem::drop;

View File

@ -1,108 +1,108 @@
error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
--> $DIR/drop_ref.rs:9:5
--> $DIR/drop_ref.rs:10:5
|
LL | drop(&SomeStruct);
| ^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::drop-ref` implied by `-D warnings`
note: argument has type `&SomeStruct`
--> $DIR/drop_ref.rs:9:10
--> $DIR/drop_ref.rs:10:10
|
LL | drop(&SomeStruct);
| ^^^^^^^^^^^
error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
--> $DIR/drop_ref.rs:12:5
--> $DIR/drop_ref.rs:13:5
|
LL | drop(&owned1);
| ^^^^^^^^^^^^^
|
note: argument has type `&SomeStruct`
--> $DIR/drop_ref.rs:12:10
--> $DIR/drop_ref.rs:13:10
|
LL | drop(&owned1);
| ^^^^^^^
error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
--> $DIR/drop_ref.rs:13:5
--> $DIR/drop_ref.rs:14:5
|
LL | drop(&&owned1);
| ^^^^^^^^^^^^^^
|
note: argument has type `&&SomeStruct`
--> $DIR/drop_ref.rs:13:10
--> $DIR/drop_ref.rs:14:10
|
LL | drop(&&owned1);
| ^^^^^^^^
error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
--> $DIR/drop_ref.rs:14:5
--> $DIR/drop_ref.rs:15:5
|
LL | drop(&mut owned1);
| ^^^^^^^^^^^^^^^^^
|
note: argument has type `&mut SomeStruct`
--> $DIR/drop_ref.rs:14:10
--> $DIR/drop_ref.rs:15:10
|
LL | drop(&mut owned1);
| ^^^^^^^^^^^
error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
--> $DIR/drop_ref.rs:18:5
--> $DIR/drop_ref.rs:19:5
|
LL | drop(reference1);
| ^^^^^^^^^^^^^^^^
|
note: argument has type `&SomeStruct`
--> $DIR/drop_ref.rs:18:10
--> $DIR/drop_ref.rs:19:10
|
LL | drop(reference1);
| ^^^^^^^^^^
error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
--> $DIR/drop_ref.rs:21:5
--> $DIR/drop_ref.rs:22:5
|
LL | drop(reference2);
| ^^^^^^^^^^^^^^^^
|
note: argument has type `&mut SomeStruct`
--> $DIR/drop_ref.rs:21:10
--> $DIR/drop_ref.rs:22:10
|
LL | drop(reference2);
| ^^^^^^^^^^
error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
--> $DIR/drop_ref.rs:24:5
--> $DIR/drop_ref.rs:25:5
|
LL | drop(reference3);
| ^^^^^^^^^^^^^^^^
|
note: argument has type `&SomeStruct`
--> $DIR/drop_ref.rs:24:10
--> $DIR/drop_ref.rs:25:10
|
LL | drop(reference3);
| ^^^^^^^^^^
error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
--> $DIR/drop_ref.rs:29:5
--> $DIR/drop_ref.rs:30:5
|
LL | drop(&val);
| ^^^^^^^^^^
|
note: argument has type `&T`
--> $DIR/drop_ref.rs:29:10
--> $DIR/drop_ref.rs:30:10
|
LL | drop(&val);
| ^^^^
error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
--> $DIR/drop_ref.rs:37:5
--> $DIR/drop_ref.rs:38:5
|
LL | std::mem::drop(&SomeStruct);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: argument has type `&SomeStruct`
--> $DIR/drop_ref.rs:37:20
--> $DIR/drop_ref.rs:38:20
|
LL | std::mem::drop(&SomeStruct);
| ^^^^^^^^^^^

View File

@ -2,34 +2,34 @@ error: strict comparison of `f32` or `f64`
--> $DIR/float_cmp.rs:65:5
|
LL | ONE as f64 != 2.0;
| ^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE as f64 - 2.0).abs() > error`
| ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin`
|
= note: `-D clippy::float-cmp` implied by `-D warnings`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64`
--> $DIR/float_cmp.rs:70:5
|
LL | x == 1.0;
| ^^^^^^^^ help: consider comparing them within some error: `(x - 1.0).abs() < error`
| ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin`
|
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64`
--> $DIR/float_cmp.rs:73:5
|
LL | twice(x) != twice(ONE as f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(twice(x) - twice(ONE as f64)).abs() > error`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin`
|
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64`
--> $DIR/float_cmp.rs:93:5
|
LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin`
|
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64` arrays
--> $DIR/float_cmp.rs:98:5
@ -37,15 +37,15 @@ error: strict comparison of `f32` or `f64` arrays
LL | a1 == a2;
| ^^^^^^^^
|
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64`
--> $DIR/float_cmp.rs:99:5
|
LL | a1[0] == a2[0];
| ^^^^^^^^^^^^^^ help: consider comparing them within some error: `(a1[0] - a2[0]).abs() < error`
| ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin`
|
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: aborting due to 6 previous errors

View File

@ -2,58 +2,58 @@ error: strict comparison of `f32` or `f64` constant
--> $DIR/float_cmp_const.rs:20:5
|
LL | 1f32 == ONE;
| ^^^^^^^^^^^ help: consider comparing them within some error: `(1f32 - ONE).abs() < error`
| ^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1f32 - ONE).abs() < error_margin`
|
= note: `-D clippy::float-cmp-const` implied by `-D warnings`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64` constant
--> $DIR/float_cmp_const.rs:21:5
|
LL | TWO == ONE;
| ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() < error`
| ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() < error_margin`
|
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64` constant
--> $DIR/float_cmp_const.rs:22:5
|
LL | TWO != ONE;
| ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() > error`
| ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() > error_margin`
|
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64` constant
--> $DIR/float_cmp_const.rs:23:5
|
LL | ONE + ONE == TWO;
| ^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE + ONE - TWO).abs() < error`
| ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE + ONE - TWO).abs() < error_margin`
|
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64` constant
--> $DIR/float_cmp_const.rs:25:5
|
LL | x as f32 == ONE;
| ^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(x as f32 - ONE).abs() < error`
| ^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x as f32 - ONE).abs() < error_margin`
|
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64` constant
--> $DIR/float_cmp_const.rs:28:5
|
LL | v == ONE;
| ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() < error`
| ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() < error_margin`
|
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64` constant
--> $DIR/float_cmp_const.rs:29:5
|
LL | v != ONE;
| ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() > error`
| ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() > error_margin`
|
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64` constant arrays
--> $DIR/float_cmp_const.rs:61:5
@ -61,7 +61,7 @@ error: strict comparison of `f32` or `f64` constant arrays
LL | NON_ZERO_ARRAY == NON_ZERO_ARRAY2;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: aborting due to 8 previous errors

View File

@ -15,7 +15,8 @@ fn main() {
x[3]; // Ok, should not produce stderr.
let y = &x;
y[0];
y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021
y[4]; // Ok, rustc will handle references too.
let v = vec![0; 5];
v[0];

View File

@ -8,15 +8,7 @@ LL | x[index];
= help: Consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic.
--> $DIR/indexing_slicing_index.rs:18:5
|
LL | y[0];
| ^^^^
|
= help: Consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic.
--> $DIR/indexing_slicing_index.rs:21:5
--> $DIR/indexing_slicing_index.rs:22:5
|
LL | v[0];
| ^^^^
@ -24,7 +16,7 @@ LL | v[0];
= help: Consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic.
--> $DIR/indexing_slicing_index.rs:22:5
--> $DIR/indexing_slicing_index.rs:23:5
|
LL | v[10];
| ^^^^^
@ -32,7 +24,7 @@ LL | v[10];
= help: Consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic.
--> $DIR/indexing_slicing_index.rs:23:5
--> $DIR/indexing_slicing_index.rs:24:5
|
LL | v[1 << 3];
| ^^^^^^^^^
@ -40,7 +32,7 @@ LL | v[1 << 3];
= help: Consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic.
--> $DIR/indexing_slicing_index.rs:29:5
--> $DIR/indexing_slicing_index.rs:30:5
|
LL | v[N];
| ^^^^
@ -48,12 +40,12 @@ LL | v[N];
= help: Consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic.
--> $DIR/indexing_slicing_index.rs:30:5
--> $DIR/indexing_slicing_index.rs:31:5
|
LL | v[M];
| ^^^^
|
= help: Consider using `.get(n)` or `.get_mut(n)` instead
error: aborting due to 7 previous errors
error: aborting due to 6 previous errors

View File

@ -71,29 +71,17 @@ LL | &x[1..][..5];
|
= help: Consider using `.get(..n)`or `.get_mut(..n)` instead
error: slicing may panic.
--> $DIR/indexing_slicing_slice.rs:24:6
|
LL | &y[1..2];
| ^^^^^^^
|
= help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead
error: slicing may panic.
--> $DIR/indexing_slicing_slice.rs:25:6
error: range is out of bounds
--> $DIR/indexing_slicing_slice.rs:25:12
|
LL | &y[0..=4];
| ^^^^^^^^
|
= help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead
| ^
error: slicing may panic.
--> $DIR/indexing_slicing_slice.rs:26:6
error: range is out of bounds
--> $DIR/indexing_slicing_slice.rs:26:11
|
LL | &y[..=4];
| ^^^^^^^
|
= help: Consider using `.get(..n)`or `.get_mut(..n)` instead
| ^
error: slicing may panic.
--> $DIR/indexing_slicing_slice.rs:31:6
@ -133,5 +121,5 @@ LL | &v[..100];
|
= help: Consider using `.get(..n)`or `.get_mut(..n)` instead
error: aborting due to 17 previous errors
error: aborting due to 16 previous errors

View File

@ -1,4 +1,4 @@
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec`
--> $DIR/into_iter_on_ref.rs:14:30
|
LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter()
@ -6,157 +6,157 @@ LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter()
|
= note: `-D clippy::into-iter-on-ref` implied by `-D warnings`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
--> $DIR/into_iter_on_ref.rs:15:46
|
LL | let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
--> $DIR/into_iter_on_ref.rs:16:41
|
LL | let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
--> $DIR/into_iter_on_ref.rs:17:44
|
LL | let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
--> $DIR/into_iter_on_ref.rs:19:32
|
LL | let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
--> $DIR/into_iter_on_ref.rs:20:36
|
LL | let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `array`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `array`
--> $DIR/into_iter_on_ref.rs:21:40
|
LL | let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut()
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Option`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Option`
--> $DIR/into_iter_on_ref.rs:23:24
|
LL | let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Option`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Option`
--> $DIR/into_iter_on_ref.rs:24:28
|
LL | let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut()
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Result`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Result`
--> $DIR/into_iter_on_ref.rs:25:32
|
LL | let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Result`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Result`
--> $DIR/into_iter_on_ref.rs:26:37
|
LL | let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ WARN equivalent to .iter_mut()
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec`
--> $DIR/into_iter_on_ref.rs:27:34
|
LL | let _ = (&Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Vec`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Vec`
--> $DIR/into_iter_on_ref.rs:28:38
|
LL | let _ = (&mut Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeMap`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeMap`
--> $DIR/into_iter_on_ref.rs:29:44
|
LL | let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `BTreeMap`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `BTreeMap`
--> $DIR/into_iter_on_ref.rs:30:48
|
LL | let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `VecDeque`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `VecDeque`
--> $DIR/into_iter_on_ref.rs:31:39
|
LL | let _ = (&VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `VecDeque`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `VecDeque`
--> $DIR/into_iter_on_ref.rs:32:43
|
LL | let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `LinkedList`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `LinkedList`
--> $DIR/into_iter_on_ref.rs:33:41
|
LL | let _ = (&LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `LinkedList`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `LinkedList`
--> $DIR/into_iter_on_ref.rs:34:45
|
LL | let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashMap`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashMap`
--> $DIR/into_iter_on_ref.rs:35:43
|
LL | let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `HashMap`
error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `HashMap`
--> $DIR/into_iter_on_ref.rs:36:47
|
LL | let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
| ^^^^^^^^^ help: call directly: `iter_mut`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeSet`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeSet`
--> $DIR/into_iter_on_ref.rs:38:39
|
LL | let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BinaryHeap`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BinaryHeap`
--> $DIR/into_iter_on_ref.rs:39:41
|
LL | let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashSet`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashSet`
--> $DIR/into_iter_on_ref.rs:40:38
|
LL | let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Path`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Path`
--> $DIR/into_iter_on_ref.rs:41:43
|
LL | let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `PathBuf`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `PathBuf`
--> $DIR/into_iter_on_ref.rs:42:47
|
LL | let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter()
| ^^^^^^^^^ help: call directly: `iter`
error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array`
error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
--> $DIR/into_iter_on_ref.rs:44:26
|
LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter()

View File

@ -33,6 +33,7 @@ fn issue985_alt() -> i32 {
x
}
#[allow(clippy::manual_strip)]
fn issue975() -> String {
let mut udn = "dummy".to_string();
if udn.starts_with("uuid:") {

View File

@ -1,5 +1,5 @@
error: `if _ { .. } else { .. }` is an expression
--> $DIR/let_if_seq.rs:63:5
--> $DIR/let_if_seq.rs:64:5
|
LL | / let mut foo = 0;
LL | | if f() {
@ -11,7 +11,7 @@ LL | | }
= note: you might not need `mut` at all
error: `if _ { .. } else { .. }` is an expression
--> $DIR/let_if_seq.rs:68:5
--> $DIR/let_if_seq.rs:69:5
|
LL | / let mut bar = 0;
LL | | if f() {
@ -25,7 +25,7 @@ LL | | }
= note: you might not need `mut` at all
error: `if _ { .. } else { .. }` is an expression
--> $DIR/let_if_seq.rs:76:5
--> $DIR/let_if_seq.rs:77:5
|
LL | / let quz;
LL | | if f() {
@ -36,7 +36,7 @@ LL | | }
| |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };`
error: `if _ { .. } else { .. }` is an expression
--> $DIR/let_if_seq.rs:105:5
--> $DIR/let_if_seq.rs:106:5
|
LL | / let mut baz = 0;
LL | | if f() {

View File

@ -0,0 +1,66 @@
#![warn(clippy::manual_strip)]
fn main() {
let s = "abc";
if s.starts_with("ab") {
str::to_string(&s["ab".len()..]);
s["ab".len()..].to_string();
str::to_string(&s[2..]);
s[2..].to_string();
}
if s.ends_with("bc") {
str::to_string(&s[..s.len() - "bc".len()]);
s[..s.len() - "bc".len()].to_string();
str::to_string(&s[..s.len() - 2]);
s[..s.len() - 2].to_string();
}
// Character patterns
if s.starts_with('a') {
str::to_string(&s[1..]);
s[1..].to_string();
}
// Variable prefix
let prefix = "ab";
if s.starts_with(prefix) {
str::to_string(&s[prefix.len()..]);
}
// Constant prefix
const PREFIX: &str = "ab";
if s.starts_with(PREFIX) {
str::to_string(&s[PREFIX.len()..]);
str::to_string(&s[2..]);
}
// Constant target
const TARGET: &str = "abc";
if TARGET.starts_with(prefix) {
str::to_string(&TARGET[prefix.len()..]);
}
// String target - not mutated.
let s1: String = "abc".into();
if s1.starts_with("ab") {
s1[2..].to_uppercase();
}
// String target - mutated. (Don't lint.)
let mut s2: String = "abc".into();
if s2.starts_with("ab") {
s2.push('d');
s2[2..].to_uppercase();
}
// Target not stripped. (Don't lint.)
let s3 = String::from("abcd");
let s4 = String::from("efgh");
if s3.starts_with("ab") {
s4[2..].to_string();
}
}

View File

@ -0,0 +1,132 @@
error: stripping a prefix manually
--> $DIR/manual_strip.rs:7:24
|
LL | str::to_string(&s["ab".len()..]);
| ^^^^^^^^^^^^^^^^
|
= note: `-D clippy::manual-strip` implied by `-D warnings`
note: the prefix was tested here
--> $DIR/manual_strip.rs:6:5
|
LL | if s.starts_with("ab") {
| ^^^^^^^^^^^^^^^^^^^^^^^
help: try using the `strip_prefix` method
|
LL | if let Some(<stripped>) = s.strip_prefix("ab") {
LL | str::to_string(<stripped>);
LL | <stripped>.to_string();
LL |
LL | str::to_string(<stripped>);
LL | <stripped>.to_string();
|
error: stripping a suffix manually
--> $DIR/manual_strip.rs:15:24
|
LL | str::to_string(&s[..s.len() - "bc".len()]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the suffix was tested here
--> $DIR/manual_strip.rs:14:5
|
LL | if s.ends_with("bc") {
| ^^^^^^^^^^^^^^^^^^^^^
help: try using the `strip_suffix` method
|
LL | if let Some(<stripped>) = s.strip_suffix("bc") {
LL | str::to_string(<stripped>);
LL | <stripped>.to_string();
LL |
LL | str::to_string(<stripped>);
LL | <stripped>.to_string();
|
error: stripping a prefix manually
--> $DIR/manual_strip.rs:24:24
|
LL | str::to_string(&s[1..]);
| ^^^^^^^
|
note: the prefix was tested here
--> $DIR/manual_strip.rs:23:5
|
LL | if s.starts_with('a') {
| ^^^^^^^^^^^^^^^^^^^^^^
help: try using the `strip_prefix` method
|
LL | if let Some(<stripped>) = s.strip_prefix('a') {
LL | str::to_string(<stripped>);
LL | <stripped>.to_string();
|
error: stripping a prefix manually
--> $DIR/manual_strip.rs:31:24
|
LL | str::to_string(&s[prefix.len()..]);
| ^^^^^^^^^^^^^^^^^^
|
note: the prefix was tested here
--> $DIR/manual_strip.rs:30:5
|
LL | if s.starts_with(prefix) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
help: try using the `strip_prefix` method
|
LL | if let Some(<stripped>) = s.strip_prefix(prefix) {
LL | str::to_string(<stripped>);
|
error: stripping a prefix manually
--> $DIR/manual_strip.rs:37:24
|
LL | str::to_string(&s[PREFIX.len()..]);
| ^^^^^^^^^^^^^^^^^^
|
note: the prefix was tested here
--> $DIR/manual_strip.rs:36:5
|
LL | if s.starts_with(PREFIX) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
help: try using the `strip_prefix` method
|
LL | if let Some(<stripped>) = s.strip_prefix(PREFIX) {
LL | str::to_string(<stripped>);
LL | str::to_string(<stripped>);
|
error: stripping a prefix manually
--> $DIR/manual_strip.rs:44:24
|
LL | str::to_string(&TARGET[prefix.len()..]);
| ^^^^^^^^^^^^^^^^^^^^^^^
|
note: the prefix was tested here
--> $DIR/manual_strip.rs:43:5
|
LL | if TARGET.starts_with(prefix) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: try using the `strip_prefix` method
|
LL | if let Some(<stripped>) = TARGET.strip_prefix(prefix) {
LL | str::to_string(<stripped>);
|
error: stripping a prefix manually
--> $DIR/manual_strip.rs:50:9
|
LL | s1[2..].to_uppercase();
| ^^^^^^^
|
note: the prefix was tested here
--> $DIR/manual_strip.rs:49:5
|
LL | if s1.starts_with("ab") {
| ^^^^^^^^^^^^^^^^^^^^^^^^
help: try using the `strip_prefix` method
|
LL | if let Some(<stripped>) = s1.strip_prefix("ab") {
LL | <stripped>.to_uppercase();
|
error: aborting due to 7 previous errors

View File

@ -0,0 +1,25 @@
#![warn(clippy::map_err_ignore)]
use std::convert::TryFrom;
use std::error::Error;
use std::fmt;
#[derive(Debug)]
enum Errors {
Ignored,
}
impl Error for Errors {}
impl fmt::Display for Errors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Error")
}
}
fn main() -> Result<(), Errors> {
let x = u32::try_from(-123_i32);
println!("{:?}", x.map_err(|_| Errors::Ignored));
Ok(())
}

View File

@ -0,0 +1,11 @@
error: `map_err(|_|...` ignores the original error
--> $DIR/map_err.rs:22:32
|
LL | println!("{:?}", x.map_err(|_| Errors::Ignored));
| ^^^
|
= note: `-D clippy::map-err-ignore` implied by `-D warnings`
= help: Consider wrapping the error in an enum variant
error: aborting due to previous error

View File

@ -0,0 +1,50 @@
#![deny(clippy::internal)]
#![feature(rustc_private)]
extern crate rustc_hir;
extern crate rustc_lint;
extern crate rustc_middle;
#[macro_use]
extern crate rustc_session;
use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
mod paths {
pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
}
mod utils {
use super::*;
pub fn match_type(_cx: &LateContext<'_>, _ty: Ty<'_>, _path: &[&str]) -> bool {
false
}
}
use utils::match_type;
declare_lint! {
pub TEST_LINT,
Warn,
""
}
declare_lint_pass!(Pass => [TEST_LINT]);
static OPTION: [&str; 3] = ["core", "option", "Option"];
impl<'tcx> LateLintPass<'tcx> for Pass {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) {
let ty = cx.typeck_results().expr_ty(expr);
let _ = match_type(cx, ty, &paths::VEC);
let _ = match_type(cx, ty, &OPTION);
let _ = match_type(cx, ty, &["core", "result", "Result"]);
let rc_path = &["alloc", "rc", "Rc"];
let _ = utils::match_type(cx, ty, rc_path);
}
}
fn main() {}

View File

@ -0,0 +1,33 @@
error: usage of `utils::match_type()` on a type diagnostic item
--> $DIR/match_type_on_diag_item.rs:41:17
|
LL | let _ = match_type(cx, ty, &paths::VEC);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))`
|
note: the lint level is defined here
--> $DIR/match_type_on_diag_item.rs:1:9
|
LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^
= note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]`
error: usage of `utils::match_type()` on a type diagnostic item
--> $DIR/match_type_on_diag_item.rs:42:17
|
LL | let _ = match_type(cx, ty, &OPTION);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(option_type))`
error: usage of `utils::match_type()` on a type diagnostic item
--> $DIR/match_type_on_diag_item.rs:43:17
|
LL | let _ = match_type(cx, ty, &["core", "result", "Result"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(result_type))`
error: usage of `utils::match_type()` on a type diagnostic item
--> $DIR/match_type_on_diag_item.rs:46:17
|
LL | let _ = utils::match_type(cx, ty, rc_path);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(Rc))`
error: aborting due to 4 previous errors

View File

@ -1,5 +1,6 @@
// run-rustfix
#![warn(clippy::option_if_let_else)]
#![allow(clippy::redundant_closure)]
fn bad1(string: Option<&str>) -> (bool, &str) {
string.map_or((false, "hello"), |x| (true, x))
@ -36,6 +37,14 @@ fn longer_body(arg: Option<u32>) -> u32 {
})
}
fn impure_else(arg: Option<i32>) {
let side_effect = || {
println!("return 1");
1
};
let _ = arg.map_or_else(|| side_effect(), |x| x);
}
fn test_map_or_else(arg: Option<u32>) {
let _ = arg.map_or_else(|| {
let mut y = 1;
@ -71,4 +80,5 @@ fn main() {
let _ = longer_body(None);
test_map_or_else(None);
let _ = negative_tests(None);
let _ = impure_else(None);
}

View File

@ -1,5 +1,6 @@
// run-rustfix
#![warn(clippy::option_if_let_else)]
#![allow(clippy::redundant_closure)]
fn bad1(string: Option<&str>) -> (bool, &str) {
if let Some(x) = string {
@ -52,6 +53,19 @@ fn longer_body(arg: Option<u32>) -> u32 {
}
}
fn impure_else(arg: Option<i32>) {
let side_effect = || {
println!("return 1");
1
};
let _ = if let Some(x) = arg {
x
} else {
// map_or_else must be suggested
side_effect()
};
}
fn test_map_or_else(arg: Option<u32>) {
let _ = if let Some(x) = arg {
x * x * x * x
@ -89,4 +103,5 @@ fn main() {
let _ = longer_body(None);
test_map_or_else(None);
let _ = negative_tests(None);
let _ = impure_else(None);
}

View File

@ -1,5 +1,5 @@
error: use Option::map_or instead of an if let/else
--> $DIR/option_if_let_else.rs:5:5
--> $DIR/option_if_let_else.rs:6:5
|
LL | / if let Some(x) = string {
LL | | (true, x)
@ -11,7 +11,7 @@ LL | | }
= note: `-D clippy::option-if-let-else` implied by `-D warnings`
error: use Option::map_or instead of an if let/else
--> $DIR/option_if_let_else.rs:15:12
--> $DIR/option_if_let_else.rs:16:12
|
LL | } else if let Some(x) = string {
| ____________^
@ -22,19 +22,19 @@ LL | | }
| |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }`
error: use Option::map_or instead of an if let/else
--> $DIR/option_if_let_else.rs:23:13
--> $DIR/option_if_let_else.rs:24:13
|
LL | let _ = if let Some(s) = *string { s.len() } else { 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())`
error: use Option::map_or instead of an if let/else
--> $DIR/option_if_let_else.rs:24:13
--> $DIR/option_if_let_else.rs:25:13
|
LL | let _ = if let Some(s) = &num { s } else { &0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
error: use Option::map_or instead of an if let/else
--> $DIR/option_if_let_else.rs:25:13
--> $DIR/option_if_let_else.rs:26:13
|
LL | let _ = if let Some(s) = &mut num {
| _____________^
@ -54,13 +54,13 @@ LL | });
|
error: use Option::map_or instead of an if let/else
--> $DIR/option_if_let_else.rs:31:13
--> $DIR/option_if_let_else.rs:32:13
|
LL | let _ = if let Some(ref s) = num { s } else { &0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
error: use Option::map_or instead of an if let/else
--> $DIR/option_if_let_else.rs:32:13
--> $DIR/option_if_let_else.rs:33:13
|
LL | let _ = if let Some(mut s) = num {
| _____________^
@ -80,7 +80,7 @@ LL | });
|
error: use Option::map_or instead of an if let/else
--> $DIR/option_if_let_else.rs:38:13
--> $DIR/option_if_let_else.rs:39:13
|
LL | let _ = if let Some(ref mut s) = num {
| _____________^
@ -100,7 +100,7 @@ LL | });
|
error: use Option::map_or instead of an if let/else
--> $DIR/option_if_let_else.rs:47:5
--> $DIR/option_if_let_else.rs:48:5
|
LL | / if let Some(x) = arg {
LL | | let y = x * x;
@ -119,7 +119,19 @@ LL | })
|
error: use Option::map_or_else instead of an if let/else
--> $DIR/option_if_let_else.rs:56:13
--> $DIR/option_if_let_else.rs:61:13
|
LL | let _ = if let Some(x) = arg {
| _____________^
LL | | x
LL | | } else {
LL | | // map_or_else must be suggested
LL | | side_effect()
LL | | };
| |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)`
error: use Option::map_or_else instead of an if let/else
--> $DIR/option_if_let_else.rs:70:13
|
LL | let _ = if let Some(x) = arg {
| _____________^
@ -142,10 +154,10 @@ LL | }, |x| x * x * x * x);
|
error: use Option::map_or instead of an if let/else
--> $DIR/option_if_let_else.rs:85:13
--> $DIR/option_if_let_else.rs:99:13
|
LL | let _ = if let Some(x) = optional { x + 2 } else { 5 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
error: aborting due to 11 previous errors
error: aborting due to 12 previous errors

View File

@ -0,0 +1,70 @@
#![warn(clippy::panic_in_result_fn)]
struct A;
impl A {
fn result_with_panic() -> Result<bool, String> // should emit lint
{
panic!("error");
}
fn result_with_unimplemented() -> Result<bool, String> // should emit lint
{
unimplemented!();
}
fn result_with_unreachable() -> Result<bool, String> // should emit lint
{
unreachable!();
}
fn result_with_todo() -> Result<bool, String> // should emit lint
{
todo!("Finish this");
}
fn other_with_panic() // should not emit lint
{
panic!("");
}
fn other_with_unreachable() // should not emit lint
{
unreachable!();
}
fn other_with_unimplemented() // should not emit lint
{
unimplemented!();
}
fn other_with_todo() // should not emit lint
{
todo!("finish this")
}
fn result_without_banned_functions() -> Result<bool, String> // should not emit lint
{
Ok(true)
}
}
fn function_result_with_panic() -> Result<bool, String> // should emit lint
{
panic!("error");
}
fn todo() {
println!("something");
}
fn function_result_with_custom_todo() -> Result<bool, String> // should not emit lint
{
todo();
Ok(true)
}
fn main() -> Result<(), String> {
todo!("finish main method");
Ok(())
}

View File

@ -0,0 +1,105 @@
error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
--> $DIR/panic_in_result_fn.rs:6:5
|
LL | / fn result_with_panic() -> Result<bool, String> // should emit lint
LL | | {
LL | | panic!("error");
LL | | }
| |_____^
|
= note: `-D clippy::panic-in-result-fn` implied by `-D warnings`
= help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
note: return Err() instead of panicking
--> $DIR/panic_in_result_fn.rs:8:9
|
LL | panic!("error");
| ^^^^^^^^^^^^^^^^
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
--> $DIR/panic_in_result_fn.rs:11:5
|
LL | / fn result_with_unimplemented() -> Result<bool, String> // should emit lint
LL | | {
LL | | unimplemented!();
LL | | }
| |_____^
|
= help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
note: return Err() instead of panicking
--> $DIR/panic_in_result_fn.rs:13:9
|
LL | unimplemented!();
| ^^^^^^^^^^^^^^^^^
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
--> $DIR/panic_in_result_fn.rs:16:5
|
LL | / fn result_with_unreachable() -> Result<bool, String> // should emit lint
LL | | {
LL | | unreachable!();
LL | | }
| |_____^
|
= help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
note: return Err() instead of panicking
--> $DIR/panic_in_result_fn.rs:18:9
|
LL | unreachable!();
| ^^^^^^^^^^^^^^^
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
--> $DIR/panic_in_result_fn.rs:21:5
|
LL | / fn result_with_todo() -> Result<bool, String> // should emit lint
LL | | {
LL | | todo!("Finish this");
LL | | }
| |_____^
|
= help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
note: return Err() instead of panicking
--> $DIR/panic_in_result_fn.rs:23:9
|
LL | todo!("Finish this");
| ^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
--> $DIR/panic_in_result_fn.rs:52:1
|
LL | / fn function_result_with_panic() -> Result<bool, String> // should emit lint
LL | | {
LL | | panic!("error");
LL | | }
| |_^
|
= help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
note: return Err() instead of panicking
--> $DIR/panic_in_result_fn.rs:54:5
|
LL | panic!("error");
| ^^^^^^^^^^^^^^^^
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
--> $DIR/panic_in_result_fn.rs:67:1
|
LL | / fn main() -> Result<(), String> {
LL | | todo!("finish main method");
LL | | Ok(())
LL | | }
| |_^
|
= help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
note: return Err() instead of panicking
--> $DIR/panic_in_result_fn.rs:68:5
|
LL | todo!("finish main method");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 6 previous errors

View File

@ -9,6 +9,7 @@ fn main() {
print!("Hello {}\n", "world");
print!("Hello {} {}\n", "world", "#2");
print!("{}\n", 1265);
print!("\n");
// these are all fine
print!("");

View File

@ -44,7 +44,18 @@ LL | println!("{}", 1265);
| ^^^^^^^ --
error: using `print!()` with a format string that ends in a single newline
--> $DIR/print_with_newline.rs:30:5
--> $DIR/print_with_newline.rs:12:5
|
LL | print!("/n");
| ^^^^^^^^^^^^
|
help: use `println!` instead
|
LL | println!();
| ^^^^^^^ --
error: using `print!()` with a format string that ends in a single newline
--> $DIR/print_with_newline.rs:31:5
|
LL | print!("//n"); // should fail
| ^^^^^^^^^^^^^^
@ -55,7 +66,7 @@ LL | println!("/"); // should fail
| ^^^^^^^ --
error: using `print!()` with a format string that ends in a single newline
--> $DIR/print_with_newline.rs:37:5
--> $DIR/print_with_newline.rs:38:5
|
LL | / print!(
LL | | "
@ -70,7 +81,7 @@ LL | ""
|
error: using `print!()` with a format string that ends in a single newline
--> $DIR/print_with_newline.rs:41:5
--> $DIR/print_with_newline.rs:42:5
|
LL | / print!(
LL | | r"
@ -85,7 +96,7 @@ LL | r""
|
error: using `print!()` with a format string that ends in a single newline
--> $DIR/print_with_newline.rs:49:5
--> $DIR/print_with_newline.rs:50:5
|
LL | print!("/r/n"); //~ ERROR
| ^^^^^^^^^^^^^^^
@ -96,7 +107,7 @@ LL | println!("/r"); //~ ERROR
| ^^^^^^^ --
error: using `print!()` with a format string that ends in a single newline
--> $DIR/print_with_newline.rs:50:5
--> $DIR/print_with_newline.rs:51:5
|
LL | print!("foo/rbar/n") // ~ ERROR
| ^^^^^^^^^^^^^^^^^^^^
@ -106,5 +117,5 @@ help: use `println!` instead
LL | println!("foo/rbar") // ~ ERROR
| ^^^^^^^ --
error: aborting due to 9 previous errors
error: aborting due to 10 previous errors

View File

@ -0,0 +1,26 @@
#![warn(clippy::rc_buffer)]
use std::cell::RefCell;
use std::ffi::OsString;
use std::path::PathBuf;
use std::rc::Rc;
struct S {
// triggers lint
bad1: Rc<String>,
bad2: Rc<PathBuf>,
bad3: Rc<Vec<u8>>,
bad4: Rc<OsString>,
// does not trigger lint
good1: Rc<RefCell<String>>,
}
// triggers lint
fn func_bad1(_: Rc<String>) {}
fn func_bad2(_: Rc<PathBuf>) {}
fn func_bad3(_: Rc<Vec<u8>>) {}
fn func_bad4(_: Rc<OsString>) {}
// does not trigger lint
fn func_good1(_: Rc<RefCell<String>>) {}
fn main() {}

View File

@ -0,0 +1,52 @@
error: usage of `Rc<T>` when T is a buffer type
--> $DIR/rc_buffer.rs:10:11
|
LL | bad1: Rc<String>,
| ^^^^^^^^^^ help: try: `Rc<str>`
|
= note: `-D clippy::rc-buffer` implied by `-D warnings`
error: usage of `Rc<T>` when T is a buffer type
--> $DIR/rc_buffer.rs:11:11
|
LL | bad2: Rc<PathBuf>,
| ^^^^^^^^^^^ help: try: `Rc<std::path::Path>`
error: usage of `Rc<T>` when T is a buffer type
--> $DIR/rc_buffer.rs:12:11
|
LL | bad3: Rc<Vec<u8>>,
| ^^^^^^^^^^^ help: try: `Rc<[u8]>`
error: usage of `Rc<T>` when T is a buffer type
--> $DIR/rc_buffer.rs:13:11
|
LL | bad4: Rc<OsString>,
| ^^^^^^^^^^^^ help: try: `Rc<std::ffi::OsStr>`
error: usage of `Rc<T>` when T is a buffer type
--> $DIR/rc_buffer.rs:19:17
|
LL | fn func_bad1(_: Rc<String>) {}
| ^^^^^^^^^^ help: try: `Rc<str>`
error: usage of `Rc<T>` when T is a buffer type
--> $DIR/rc_buffer.rs:20:17
|
LL | fn func_bad2(_: Rc<PathBuf>) {}
| ^^^^^^^^^^^ help: try: `Rc<std::path::Path>`
error: usage of `Rc<T>` when T is a buffer type
--> $DIR/rc_buffer.rs:21:17
|
LL | fn func_bad3(_: Rc<Vec<u8>>) {}
| ^^^^^^^^^^^ help: try: `Rc<[u8]>`
error: usage of `Rc<T>` when T is a buffer type
--> $DIR/rc_buffer.rs:22:17
|
LL | fn func_bad4(_: Rc<OsString>) {}
| ^^^^^^^^^^^^ help: try: `Rc<std::ffi::OsStr>`
error: aborting due to 8 previous errors

View File

@ -0,0 +1,25 @@
#![warn(clippy::rc_buffer)]
use std::ffi::OsString;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
struct S {
// triggers lint
bad1: Arc<String>,
bad2: Arc<PathBuf>,
bad3: Arc<Vec<u8>>,
bad4: Arc<OsString>,
// does not trigger lint
good1: Arc<Mutex<String>>,
}
// triggers lint
fn func_bad1(_: Arc<String>) {}
fn func_bad2(_: Arc<PathBuf>) {}
fn func_bad3(_: Arc<Vec<u8>>) {}
fn func_bad4(_: Arc<OsString>) {}
// does not trigger lint
fn func_good1(_: Arc<Mutex<String>>) {}
fn main() {}

View File

@ -0,0 +1,52 @@
error: usage of `Arc<T>` when T is a buffer type
--> $DIR/rc_buffer_arc.rs:9:11
|
LL | bad1: Arc<String>,
| ^^^^^^^^^^^ help: try: `Arc<str>`
|
= note: `-D clippy::rc-buffer` implied by `-D warnings`
error: usage of `Arc<T>` when T is a buffer type
--> $DIR/rc_buffer_arc.rs:10:11
|
LL | bad2: Arc<PathBuf>,
| ^^^^^^^^^^^^ help: try: `Arc<std::path::Path>`
error: usage of `Arc<T>` when T is a buffer type
--> $DIR/rc_buffer_arc.rs:11:11
|
LL | bad3: Arc<Vec<u8>>,
| ^^^^^^^^^^^^ help: try: `Arc<[u8]>`
error: usage of `Arc<T>` when T is a buffer type
--> $DIR/rc_buffer_arc.rs:12:11
|
LL | bad4: Arc<OsString>,
| ^^^^^^^^^^^^^ help: try: `Arc<std::ffi::OsStr>`
error: usage of `Arc<T>` when T is a buffer type
--> $DIR/rc_buffer_arc.rs:18:17
|
LL | fn func_bad1(_: Arc<String>) {}
| ^^^^^^^^^^^ help: try: `Arc<str>`
error: usage of `Arc<T>` when T is a buffer type
--> $DIR/rc_buffer_arc.rs:19:17
|
LL | fn func_bad2(_: Arc<PathBuf>) {}
| ^^^^^^^^^^^^ help: try: `Arc<std::path::Path>`
error: usage of `Arc<T>` when T is a buffer type
--> $DIR/rc_buffer_arc.rs:20:17
|
LL | fn func_bad3(_: Arc<Vec<u8>>) {}
| ^^^^^^^^^^^^ help: try: `Arc<[u8]>`
error: usage of `Arc<T>` when T is a buffer type
--> $DIR/rc_buffer_arc.rs:21:17
|
LL | fn func_bad4(_: Arc<OsString>) {}
| ^^^^^^^^^^^^^ help: try: `Arc<std::ffi::OsStr>`
error: aborting due to 8 previous errors

View File

@ -0,0 +1,12 @@
#![warn(clippy::rc_buffer)]
use std::rc::Rc;
struct String;
struct S {
// does not trigger lint
good1: Rc<String>,
}
fn main() {}

View File

@ -18,39 +18,14 @@ fn main() {
if Err::<i32, i32>(42).is_err() {}
if None::<()>.is_none() {}
if Some(42).is_some() {}
if Some(42).is_some() {
foo();
} else {
bar();
}
while Some(42).is_some() {}
while Some(42).is_none() {}
while None::<()>.is_none() {}
while Ok::<i32, i32>(10).is_ok() {}
while Ok::<i32, i32>(10).is_err() {}
let mut v = vec![1, 2, 3];
while v.pop().is_some() {
foo();
}
if Ok::<i32, i32>(42).is_ok() {}
if Err::<i32, i32>(42).is_err() {}
if None::<i32>.is_none() {}
if Some(42).is_some() {}
if let Ok(x) = Ok::<i32, i32>(42) {
println!("{}", x);
}
@ -63,48 +38,25 @@ fn main() {
Err::<i32, i32>(42).is_ok();
Some(42).is_some();
None::<()>.is_none();
let _ = None::<()>.is_none();
let _ = if Ok::<usize, ()>(4).is_ok() { true } else { false };
let opt = Some(false);
let x = if opt.is_some() { true } else { false };
takes_bool(x);
issue5504();
issue6067();
issue6065();
let _ = if gen_opt().is_some() {
let _ = if gen_res().is_ok() {
1
} else if gen_opt().is_none() {
2
} else if gen_res().is_ok() {
3
} else if gen_res().is_err() {
4
2
} else {
5
3
};
}
fn gen_opt() -> Option<()> {
None
}
fn gen_res() -> Result<(), ()> {
Ok(())
}
fn takes_bool(_: bool) {}
fn foo() {}
fn bar() {}
macro_rules! m {
() => {
Some(42u32)
@ -128,31 +80,30 @@ fn issue5504() {
while m!().is_some() {}
}
fn issue6065() {
macro_rules! if_let_in_macro {
($pat:pat, $x:expr) => {
if let Some($pat) = $x {}
};
}
// shouldn't be linted
if_let_in_macro!(_, Some(42));
}
// Methods that are unstable const should not be suggested within a const context, see issue #5697.
// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result`, and `is_some` and `is_none`
// of `Option` were stabilized as const, so the following should be linted.
// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const,
// so the following should be linted.
const fn issue6067() {
if Ok::<i32, i32>(42).is_ok() {}
if Err::<i32, i32>(42).is_err() {}
if Some(42).is_some() {}
if None::<()>.is_none() {}
while Ok::<i32, i32>(10).is_ok() {}
while Ok::<i32, i32>(10).is_err() {}
while Some(42).is_some() {}
while None::<()>.is_none() {}
Ok::<i32, i32>(42).is_ok();
Err::<i32, i32>(42).is_err();
Some(42).is_some();
None::<()>.is_none();
}

View File

@ -18,39 +18,14 @@ fn main() {
if let Err(_) = Err::<i32, i32>(42) {}
if let None = None::<()> {}
if let Some(_) = Some(42) {}
if let Some(_) = Some(42) {
foo();
} else {
bar();
}
while let Some(_) = Some(42) {}
while let None = Some(42) {}
while let None = None::<()> {}
while let Ok(_) = Ok::<i32, i32>(10) {}
while let Err(_) = Ok::<i32, i32>(10) {}
let mut v = vec![1, 2, 3];
while let Some(_) = v.pop() {
foo();
}
if Ok::<i32, i32>(42).is_ok() {}
if Err::<i32, i32>(42).is_err() {}
if None::<i32>.is_none() {}
if Some(42).is_some() {}
if let Ok(x) = Ok::<i32, i32>(42) {
println!("{}", x);
}
@ -75,57 +50,25 @@ fn main() {
Err(_) => false,
};
match Some(42) {
Some(_) => true,
None => false,
};
match None::<()> {
Some(_) => false,
None => true,
};
let _ = match None::<()> {
Some(_) => false,
None => true,
};
let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
let opt = Some(false);
let x = if let Some(_) = opt { true } else { false };
takes_bool(x);
issue5504();
issue6067();
issue6065();
let _ = if let Some(_) = gen_opt() {
let _ = if let Ok(_) = gen_res() {
1
} else if let None = gen_opt() {
2
} else if let Ok(_) = gen_res() {
3
} else if let Err(_) = gen_res() {
4
2
} else {
5
3
};
}
fn gen_opt() -> Option<()> {
None
}
fn gen_res() -> Result<(), ()> {
Ok(())
}
fn takes_bool(_: bool) {}
fn foo() {}
fn bar() {}
macro_rules! m {
() => {
Some(42u32)
@ -149,26 +92,29 @@ fn issue5504() {
while let Some(_) = m!() {}
}
fn issue6065() {
macro_rules! if_let_in_macro {
($pat:pat, $x:expr) => {
if let Some($pat) = $x {}
};
}
// shouldn't be linted
if_let_in_macro!(_, Some(42));
}
// Methods that are unstable const should not be suggested within a const context, see issue #5697.
// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result`, and `is_some` and `is_none`
// of `Option` were stabilized as const, so the following should be linted.
// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const,
// so the following should be linted.
const fn issue6067() {
if let Ok(_) = Ok::<i32, i32>(42) {}
if let Err(_) = Err::<i32, i32>(42) {}
if let Some(_) = Some(42) {}
if let None = None::<()> {}
while let Ok(_) = Ok::<i32, i32>(10) {}
while let Err(_) = Ok::<i32, i32>(10) {}
while let Some(_) = Some(42) {}
while let None = None::<()> {}
match Ok::<i32, i32>(42) {
Ok(_) => true,
Err(_) => false,
@ -178,14 +124,4 @@ const fn issue6067() {
Ok(_) => false,
Err(_) => true,
};
match Some(42) {
Some(_) => true,
None => false,
};
match None::<()> {
Some(_) => false,
None => true,
};
}

View File

@ -18,62 +18,20 @@ error: redundant pattern matching, consider using `is_err()`
LL | if let Err(_) = Err::<i32, i32>(42) {}
| -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching.rs:21:12
|
LL | if let None = None::<()> {}
| -------^^^^------------- help: try this: `if None::<()>.is_none()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:23:12
|
LL | if let Some(_) = Some(42) {}
| -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:25:12
|
LL | if let Some(_) = Some(42) {
| -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:31:15
|
LL | while let Some(_) = Some(42) {}
| ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching.rs:33:15
|
LL | while let None = Some(42) {}
| ----------^^^^----------- help: try this: `while Some(42).is_none()`
error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching.rs:35:15
|
LL | while let None = None::<()> {}
| ----------^^^^------------- help: try this: `while None::<()>.is_none()`
error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching.rs:37:15
--> $DIR/redundant_pattern_matching.rs:21:15
|
LL | while let Ok(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching.rs:39:15
--> $DIR/redundant_pattern_matching.rs:23:15
|
LL | while let Err(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:42:15
|
LL | while let Some(_) = v.pop() {
| ----------^^^^^^^---------- help: try this: `while v.pop().is_some()`
error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching.rs:58:5
--> $DIR/redundant_pattern_matching.rs:33:5
|
LL | / match Ok::<i32, i32>(42) {
LL | | Ok(_) => true,
@ -82,7 +40,7 @@ LL | | };
| |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching.rs:63:5
--> $DIR/redundant_pattern_matching.rs:38:5
|
LL | / match Ok::<i32, i32>(42) {
LL | | Ok(_) => false,
@ -91,7 +49,7 @@ LL | | };
| |_____^ help: try this: `Ok::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching.rs:68:5
--> $DIR/redundant_pattern_matching.rs:43:5
|
LL | / match Err::<i32, i32>(42) {
LL | | Ok(_) => false,
@ -100,7 +58,7 @@ LL | | };
| |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching.rs:73:5
--> $DIR/redundant_pattern_matching.rs:48:5
|
LL | / match Err::<i32, i32>(42) {
LL | | Ok(_) => true,
@ -108,144 +66,74 @@ LL | | Err(_) => false,
LL | | };
| |_____^ help: try this: `Err::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:78:5
|
LL | / match Some(42) {
LL | | Some(_) => true,
LL | | None => false,
LL | | };
| |_____^ help: try this: `Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching.rs:83:5
|
LL | / match None::<()> {
LL | | Some(_) => false,
LL | | None => true,
LL | | };
| |_____^ help: try this: `None::<()>.is_none()`
error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching.rs:88:13
|
LL | let _ = match None::<()> {
| _____________^
LL | | Some(_) => false,
LL | | None => true,
LL | | };
| |_____^ help: try this: `None::<()>.is_none()`
error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching.rs:93:20
--> $DIR/redundant_pattern_matching.rs:53:20
|
LL | let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
| -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:96:20
|
LL | let x = if let Some(_) = opt { true } else { false };
| -------^^^^^^^------ help: try this: `if opt.is_some()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:102:20
|
LL | let _ = if let Some(_) = gen_opt() {
| -------^^^^^^^------------ help: try this: `if gen_opt().is_some()`
error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching.rs:104:19
|
LL | } else if let None = gen_opt() {
| -------^^^^------------ help: try this: `if gen_opt().is_none()`
error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching.rs:106:19
--> $DIR/redundant_pattern_matching.rs:59:20
|
LL | } else if let Ok(_) = gen_res() {
| -------^^^^^------------ help: try this: `if gen_res().is_ok()`
LL | let _ = if let Ok(_) = gen_res() {
| -------^^^^^------------ help: try this: `if gen_res().is_ok()`
error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching.rs:108:19
--> $DIR/redundant_pattern_matching.rs:61:19
|
LL | } else if let Err(_) = gen_res() {
| -------^^^^^^------------ help: try this: `if gen_res().is_err()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:141:19
--> $DIR/redundant_pattern_matching.rs:84:19
|
LL | while let Some(_) = r#try!(result_opt()) {}
| ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:142:16
--> $DIR/redundant_pattern_matching.rs:85:16
|
LL | if let Some(_) = r#try!(result_opt()) {}
| -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:148:12
--> $DIR/redundant_pattern_matching.rs:91:12
|
LL | if let Some(_) = m!() {}
| -------^^^^^^^------- help: try this: `if m!().is_some()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:149:15
--> $DIR/redundant_pattern_matching.rs:92:15
|
LL | while let Some(_) = m!() {}
| ----------^^^^^^^------- help: try this: `while m!().is_some()`
error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching.rs:156:12
--> $DIR/redundant_pattern_matching.rs:110:12
|
LL | if let Ok(_) = Ok::<i32, i32>(42) {}
| -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching.rs:158:12
--> $DIR/redundant_pattern_matching.rs:112:12
|
LL | if let Err(_) = Err::<i32, i32>(42) {}
| -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:160:12
|
LL | if let Some(_) = Some(42) {}
| -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching.rs:162:12
|
LL | if let None = None::<()> {}
| -------^^^^------------- help: try this: `if None::<()>.is_none()`
error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching.rs:164:15
--> $DIR/redundant_pattern_matching.rs:114:15
|
LL | while let Ok(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching.rs:166:15
--> $DIR/redundant_pattern_matching.rs:116:15
|
LL | while let Err(_) = Ok::<i32, i32>(10) {}
| ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:168:15
|
LL | while let Some(_) = Some(42) {}
| ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching.rs:170:15
|
LL | while let None = None::<()> {}
| ----------^^^^------------- help: try this: `while None::<()>.is_none()`
error: redundant pattern matching, consider using `is_ok()`
--> $DIR/redundant_pattern_matching.rs:172:5
--> $DIR/redundant_pattern_matching.rs:118:5
|
LL | / match Ok::<i32, i32>(42) {
LL | | Ok(_) => true,
@ -254,7 +142,7 @@ LL | | };
| |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
error: redundant pattern matching, consider using `is_err()`
--> $DIR/redundant_pattern_matching.rs:177:5
--> $DIR/redundant_pattern_matching.rs:123:5
|
LL | / match Err::<i32, i32>(42) {
LL | | Ok(_) => false,
@ -262,23 +150,5 @@ LL | | Err(_) => true,
LL | | };
| |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching.rs:182:5
|
LL | / match Some(42) {
LL | | Some(_) => true,
LL | | None => false,
LL | | };
| |_____^ help: try this: `Some(42).is_some()`
error: redundant pattern matching, consider using `is_none()`
--> $DIR/redundant_pattern_matching.rs:187:5
|
LL | / match None::<()> {
LL | | Some(_) => false,
LL | | None => true,
LL | | };
| |_____^ help: try this: `None::<()>.is_none()`
error: aborting due to 41 previous errors
error: aborting due to 22 previous errors

View File

@ -0,0 +1,85 @@
// run-rustfix
#![warn(clippy::all)]
#![warn(clippy::redundant_pattern_matching)]
#![allow(
clippy::unit_arg,
unused_must_use,
clippy::needless_bool,
clippy::match_like_matches_macro,
deprecated
)]
fn main() {
if None::<()>.is_none() {}
if Some(42).is_some() {}
if Some(42).is_some() {
foo();
} else {
bar();
}
while Some(42).is_some() {}
while Some(42).is_none() {}
while None::<()>.is_none() {}
let mut v = vec![1, 2, 3];
while v.pop().is_some() {
foo();
}
if None::<i32>.is_none() {}
if Some(42).is_some() {}
Some(42).is_some();
None::<()>.is_none();
let _ = None::<()>.is_none();
let opt = Some(false);
let x = if opt.is_some() { true } else { false };
takes_bool(x);
issue6067();
let _ = if gen_opt().is_some() {
1
} else if gen_opt().is_none() {
2
} else {
3
};
}
fn gen_opt() -> Option<()> {
None
}
fn takes_bool(_: bool) {}
fn foo() {}
fn bar() {}
// Methods that are unstable const should not be suggested within a const context, see issue #5697.
// However, in Rust 1.48.0 the methods `is_some` and `is_none` of `Option` were stabilized as const,
// so the following should be linted.
const fn issue6067() {
if Some(42).is_some() {}
if None::<()>.is_none() {}
while Some(42).is_some() {}
while None::<()>.is_none() {}
Some(42).is_some();
None::<()>.is_none();
}

View File

@ -0,0 +1,100 @@
// run-rustfix
#![warn(clippy::all)]
#![warn(clippy::redundant_pattern_matching)]
#![allow(
clippy::unit_arg,
unused_must_use,
clippy::needless_bool,
clippy::match_like_matches_macro,
deprecated
)]
fn main() {
if let None = None::<()> {}
if let Some(_) = Some(42) {}
if let Some(_) = Some(42) {
foo();
} else {
bar();
}
while let Some(_) = Some(42) {}
while let None = Some(42) {}
while let None = None::<()> {}
let mut v = vec![1, 2, 3];
while let Some(_) = v.pop() {
foo();
}
if None::<i32>.is_none() {}
if Some(42).is_some() {}
match Some(42) {
Some(_) => true,
None => false,
};
match None::<()> {
Some(_) => false,
None => true,
};
let _ = match None::<()> {
Some(_) => false,
None => true,
};
let opt = Some(false);
let x = if let Some(_) = opt { true } else { false };
takes_bool(x);
issue6067();
let _ = if let Some(_) = gen_opt() {
1
} else if let None = gen_opt() {
2
} else {
3
};
}
fn gen_opt() -> Option<()> {
None
}
fn takes_bool(_: bool) {}
fn foo() {}
fn bar() {}
// Methods that are unstable const should not be suggested within a const context, see issue #5697.
// However, in Rust 1.48.0 the methods `is_some` and `is_none` of `Option` were stabilized as const,
// so the following should be linted.
const fn issue6067() {
if let Some(_) = Some(42) {}
if let None = None::<()> {}
while let Some(_) = Some(42) {}
while let None = None::<()> {}
match Some(42) {
Some(_) => true,
None => false,
};
match None::<()> {
Some(_) => false,
None => true,
};
}

Some files were not shown because too many files have changed in this diff Show More