mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 23:34:48 +00:00
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:
commit
fadf025723
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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`]
|
||||
|
||||
|
@ -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()
|
||||
)
|
||||
|
@ -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"] }
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
}
|
||||
|
||||
|
@ -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, _) =
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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 = ¶ms[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))
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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, &[]) {
|
||||
|
@ -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
|
||||
|
@ -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(¯o_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),
|
||||
]);
|
||||
|
@ -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",
|
||||
|
245
src/tools/clippy/clippy_lints/src/manual_strip.rs
Normal file
245
src/tools/clippy/clippy_lints/src/manual_strip.rs
Normal 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
|
||||
}
|
147
src/tools/clippy/clippy_lints/src/map_err_ignore.rs
Normal file
147
src/tools/clippy/clippy_lints/src/map_err_ignore.rs
Normal 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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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 });
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
90
src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
Normal file
90
src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
Normal 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");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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(_, _))
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
128
src/tools/clippy/clippy_lints/src/utils/eager_or_lazy.rs
Normal file
128
src/tools/clippy/clippy_lints/src/utils/eager_or_lazy.rs
Normal 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)
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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"];
|
||||
|
@ -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:
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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",
|
||||
|
45
src/tools/clippy/tests/ui/atomic_ordering_exchange.rs
Normal file
45
src/tools/clippy/tests/ui/atomic_ordering_exchange.rs
Normal 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);
|
||||
}
|
131
src/tools/clippy/tests/ui/atomic_ordering_exchange.stderr
Normal file
131
src/tools/clippy/tests/ui/atomic_ordering_exchange.stderr
Normal 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
|
||||
|
47
src/tools/clippy/tests/ui/atomic_ordering_exchange_weak.rs
Normal file
47
src/tools/clippy/tests/ui/atomic_ordering_exchange_weak.rs
Normal 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);
|
||||
}
|
131
src/tools/clippy/tests/ui/atomic_ordering_exchange_weak.stderr
Normal file
131
src/tools/clippy/tests/ui/atomic_ordering_exchange_weak.stderr
Normal 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
|
||||
|
45
src/tools/clippy/tests/ui/atomic_ordering_fetch_update.rs
Normal file
45
src/tools/clippy/tests/ui/atomic_ordering_fetch_update.rs
Normal 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));
|
||||
}
|
131
src/tools/clippy/tests/ui/atomic_ordering_fetch_update.stderr
Normal file
131
src/tools/clippy/tests/ui/atomic_ordering_fetch_update.stderr
Normal 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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
||||
|
@ -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() {}
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![warn(clippy::drop_ref)]
|
||||
#![allow(clippy::toplevel_ref_arg)]
|
||||
#![allow(clippy::map_err_ignore)]
|
||||
|
||||
use std::mem::drop;
|
||||
|
||||
|
@ -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);
|
||||
| ^^^^^^^^^^^
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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:") {
|
||||
|
@ -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() {
|
||||
|
66
src/tools/clippy/tests/ui/manual_strip.rs
Normal file
66
src/tools/clippy/tests/ui/manual_strip.rs
Normal 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();
|
||||
}
|
||||
}
|
132
src/tools/clippy/tests/ui/manual_strip.stderr
Normal file
132
src/tools/clippy/tests/ui/manual_strip.stderr
Normal 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
|
||||
|
25
src/tools/clippy/tests/ui/map_err.rs
Normal file
25
src/tools/clippy/tests/ui/map_err.rs
Normal 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(())
|
||||
}
|
11
src/tools/clippy/tests/ui/map_err.stderr
Normal file
11
src/tools/clippy/tests/ui/map_err.stderr
Normal 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
|
||||
|
50
src/tools/clippy/tests/ui/match_type_on_diag_item.rs
Normal file
50
src/tools/clippy/tests/ui/match_type_on_diag_item.rs
Normal 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() {}
|
33
src/tools/clippy/tests/ui/match_type_on_diag_item.stderr
Normal file
33
src/tools/clippy/tests/ui/match_type_on_diag_item.stderr
Normal 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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
70
src/tools/clippy/tests/ui/panic_in_result_fn.rs
Normal file
70
src/tools/clippy/tests/ui/panic_in_result_fn.rs
Normal 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(())
|
||||
}
|
105
src/tools/clippy/tests/ui/panic_in_result_fn.stderr
Normal file
105
src/tools/clippy/tests/ui/panic_in_result_fn.stderr
Normal 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
|
||||
|
@ -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!("");
|
||||
|
@ -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
|
||||
|
||||
|
26
src/tools/clippy/tests/ui/rc_buffer.rs
Normal file
26
src/tools/clippy/tests/ui/rc_buffer.rs
Normal 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() {}
|
52
src/tools/clippy/tests/ui/rc_buffer.stderr
Normal file
52
src/tools/clippy/tests/ui/rc_buffer.stderr
Normal 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
|
||||
|
25
src/tools/clippy/tests/ui/rc_buffer_arc.rs
Normal file
25
src/tools/clippy/tests/ui/rc_buffer_arc.rs
Normal 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() {}
|
52
src/tools/clippy/tests/ui/rc_buffer_arc.stderr
Normal file
52
src/tools/clippy/tests/ui/rc_buffer_arc.stderr
Normal 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
|
||||
|
12
src/tools/clippy/tests/ui/rc_buffer_redefined_string.rs
Normal file
12
src/tools/clippy/tests/ui/rc_buffer_redefined_string.rs
Normal 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() {}
|
@ -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();
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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();
|
||||
}
|
100
src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
Normal file
100
src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
Normal 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
Loading…
Reference in New Issue
Block a user