Merge branch 'master' into needless-lifetime

This commit is contained in:
Tim Nielens 2020-09-29 22:51:59 +02:00
commit 7cfe3dc2eb
193 changed files with 4825 additions and 1629 deletions

View file

@ -1547,6 +1547,7 @@ Released 2018-09-13
[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
@ -1558,6 +1559,7 @@ Released 2018-09-13
[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
@ -1671,10 +1673,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
@ -1754,6 +1758,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
@ -1773,6 +1778,7 @@ Released 2018-09-13
[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure

View file

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

View file

@ -5,7 +5,7 @@
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
[There are over 350 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
[There are over 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,51 @@
use crate::utils::{match_def_path, paths, snippet, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead.
///
/// **Why is this bad?** Sometimes `std::fs::crate_dir` is mistakenly chosen over `std::fs::create_dir_all`.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// std::fs::create_dir("foo");
/// ```
/// Use instead:
/// ```rust
/// std::fs::create_dir_all("foo");
/// ```
pub CREATE_DIR,
restriction,
"calling `std::fs::create_dir` instead of `std::fs::create_dir_all`"
}
declare_lint_pass!(CreateDir => [CREATE_DIR]);
impl LateLintPass<'_> for CreateDir {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if let ExprKind::Call(ref func, ref args) = expr.kind;
if let ExprKind::Path(ref path) = func.kind;
if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::STD_FS_CREATE_DIR);
then {
span_lint_and_sugg(
cx,
CREATE_DIR,
expr.span,
"calling `std::fs::create_dir` where there may be a better way",
"consider calling `std::fs::create_dir_all` instead",
format!("create_dir_all({})", snippet(cx, args[0].span, "..")),
Applicability::MaybeIncorrect,
)
}
}
}
}

View file

@ -0,0 +1,73 @@
use crate::utils::span_lint;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol;
declare_clippy_lint! {
/// **What it does:** Lints for specific trait methods defined in clippy.toml
///
/// **Why is this bad?** Some methods are undesirable in certain contexts,
/// and it would be beneficial to lint for them as needed.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust,ignore
/// // example code where clippy issues a warning
/// foo.bad_method(); // Foo::bad_method is disallowed in the configuration
/// ```
/// Use instead:
/// ```rust,ignore
/// // example code which does not raise clippy warning
/// goodStruct.bad_method(); // GoodStruct::bad_method is not disallowed
/// ```
pub DISALLOWED_METHOD,
nursery,
"use of a disallowed method call"
}
#[derive(Clone, Debug)]
pub struct DisallowedMethod {
disallowed: FxHashSet<Vec<Symbol>>,
}
impl DisallowedMethod {
pub fn new(disallowed: &FxHashSet<String>) -> Self {
Self {
disallowed: disallowed
.iter()
.map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
.collect(),
}
}
}
impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::MethodCall(_path, _, _args, _) = &expr.kind {
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
let method_call = cx.get_def_path(def_id);
if self.disallowed.contains(&method_call) {
let method = method_call
.iter()
.map(|s| s.to_ident_string())
.collect::<Vec<_>>()
.join("::");
span_lint(
cx,
DISALLOWED_METHOD,
expr.span,
&format!("use of a disallowed method `{}`", method),
);
}
}
}
}

View file

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

View file

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

View file

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

View file

@ -285,7 +285,7 @@ impl EarlyLintPass for EnumVariantNames {
);
}
}
if item.vis.node.is_pub() {
if item.vis.kind.is_pub() {
let matching = partial_match(mod_camel, &item_camel);
let rmatching = partial_rmatch(mod_camel, &item_camel);
let nchars = mod_camel.chars().count();
@ -316,7 +316,7 @@ impl EarlyLintPass for EnumVariantNames {
}
}
if let ItemKind::Enum(ref def, _) = item.kind {
let lint = match item.vis.node {
let lint = match item.vis.kind {
VisibilityKind::Public => PUB_ENUM_VARIANT_NAMES,
_ => ENUM_VARIANT_NAMES,
};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,6 +6,7 @@
#![feature(concat_idents)]
#![feature(crate_visibility_modifier)]
#![feature(drain_filter)]
#![feature(in_band_lifetimes)]
#![feature(or_patterns)]
#![feature(rustc_private)]
#![feature(stmt_expr_attributes)]
@ -170,10 +171,12 @@ mod collapsible_if;
mod comparison_chain;
mod copies;
mod copy_iterator;
mod create_dir;
mod dbg_macro;
mod default_trait_access;
mod dereference;
mod derive;
mod disallowed_method;
mod doc;
mod double_comparison;
mod double_parens;
@ -229,7 +232,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;
@ -268,6 +273,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;
@ -513,6 +519,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&copies::MATCH_SAME_ARMS,
&copies::SAME_FUNCTIONS_IN_IF_CONDITION,
&copy_iterator::COPY_ITERATOR,
&create_dir::CREATE_DIR,
&dbg_macro::DBG_MACRO,
&default_trait_access::DEFAULT_TRAIT_ACCESS,
&dereference::EXPLICIT_DEREF_METHODS,
@ -520,6 +527,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&derive::DERIVE_ORD_XOR_PARTIAL_ORD,
&derive::EXPL_IMPL_CLONE_ON_COPY,
&derive::UNSAFE_DERIVE_DESERIALIZE,
&disallowed_method::DISALLOWED_METHOD,
&doc::DOC_MARKDOWN,
&doc::MISSING_ERRORS_DOC,
&doc::MISSING_SAFETY_DOC,
@ -623,7 +631,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,
@ -749,6 +759,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,
@ -833,6 +844,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,
@ -861,6 +873,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,
@ -916,6 +929,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);
@ -1044,6 +1058,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
store.register_early_pass(|| box precedence::Precedence);
store.register_early_pass(|| box needless_continue::NeedlessContinue);
store.register_late_pass(|| box create_dir::CreateDir);
store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType);
store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes);
store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
@ -1088,6 +1103,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,
@ -1102,11 +1119,17 @@ 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);
let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
LintId::of(&arithmetic::INTEGER_ARITHMETIC),
LintId::of(&as_conversions::AS_CONVERSIONS),
LintId::of(&create_dir::CREATE_DIR),
LintId::of(&dbg_macro::DBG_MACRO),
LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
LintId::of(&exit::EXIT),
@ -1131,6 +1154,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),
@ -1148,6 +1172,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),
@ -1176,6 +1201,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP),
LintId::of(&loops::EXPLICIT_ITER_LOOP),
LintId::of(&macro_use::MACRO_USE_IMPORTS),
LintId::of(&map_err_ignore::MAP_ERR_IGNORE),
LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
LintId::of(&matches::MATCH_BOOL),
LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
@ -1226,6 +1252,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),
]);
@ -1245,7 +1272,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),
@ -1326,6 +1352,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),
@ -1470,6 +1497,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),
@ -1503,7 +1531,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),
@ -1618,6 +1645,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),
@ -1771,6 +1799,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),
]);
@ -1784,6 +1813,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
LintId::of(&cognitive_complexity::COGNITIVE_COMPLEXITY),
LintId::of(&disallowed_method::DISALLOWED_METHOD),
LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM),
LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS),
LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS),

View file

@ -1114,7 +1114,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&
if let Some(self_expr) = args.get(0);
if let Some(pushed_item) = args.get(1);
// Check that the method being called is push() on a Vec
if match_type(cx, cx.typeck_results().expr_ty(self_expr), &paths::VEC);
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym!(vec_type));
if path.ident.name.as_str() == "push";
then {
return Some((self_expr, pushed_item))
@ -1131,6 +1131,27 @@ fn detect_same_item_push<'tcx>(
body: &'tcx Expr<'_>,
_: &'tcx Expr<'_>,
) {
fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) {
let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
span_lint_and_help(
cx,
SAME_ITEM_PUSH,
vec.span,
"it looks like the same item is being pushed into this Vec",
None,
&format!(
"try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
item_str, vec_str, item_str
),
)
}
if !matches!(pat.kind, PatKind::Wild) {
return;
}
// Determine whether it is safe to lint the body
let mut same_item_push_visitor = SameItemPushVisitor {
should_lint: true,
@ -1149,43 +1170,41 @@ fn detect_same_item_push<'tcx>(
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
{
// Make sure that the push does not involve possibly mutating values
if let PatKind::Wild = pat.kind {
let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
if let ExprKind::Path(ref qpath) = pushed_item.kind {
if_chain! {
if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id);
let node = cx.tcx.hir().get(hir_id);
if let Node::Binding(pat) = node;
if let PatKind::Binding(bind_ann, ..) = pat.kind;
if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
then {
span_lint_and_help(
cx,
SAME_ITEM_PUSH,
vec.span,
"it looks like the same item is being pushed into this Vec",
None,
&format!(
"try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
item_str, vec_str, item_str
),
)
}
match pushed_item.kind {
ExprKind::Path(ref qpath) => {
match qpath_res(cx, qpath, pushed_item.hir_id) {
// immutable bindings that are initialized with literal or constant
Res::Local(hir_id) => {
if_chain! {
let node = cx.tcx.hir().get(hir_id);
if let Node::Binding(pat) = node;
if let PatKind::Binding(bind_ann, ..) = pat.kind;
if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
let parent_node = cx.tcx.hir().get_parent_node(hir_id);
if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node);
if let Some(init) = parent_let_expr.init;
then {
match init.kind {
// immutable bindings that are initialized with literal
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
// immutable bindings that are initialized with constant
ExprKind::Path(ref path) => {
if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) {
emit_lint(cx, vec, pushed_item);
}
}
_ => {},
}
}
}
},
// constant
Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item),
_ => {},
}
} else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) {
span_lint_and_help(
cx,
SAME_ITEM_PUSH,
vec.span,
"it looks like the same item is being pushed into this Vec",
None,
&format!(
"try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
item_str, vec_str, item_str
),
)
}
},
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
_ => {},
}
}
}
@ -2115,7 +2134,7 @@ enum VarState {
DontWarn,
}
/// Scan a for loop for variables that are incremented exactly once.
/// Scan a for loop for variables that are incremented exactly once and not used after that.
struct IncrementVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>, // context reference
states: FxHashMap<HirId, VarState>, // incremented variables
@ -2135,6 +2154,10 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
if let Some(def_id) = var_def_id(self.cx, expr) {
if let Some(parent) = get_parent_expr(self.cx, expr) {
let state = self.states.entry(def_id).or_insert(VarState::Initial);
if *state == VarState::IncrOnce {
*state = VarState::DontWarn;
return;
}
match parent.kind {
ExprKind::AssignOp(op, ref lhs, ref rhs) => {
@ -2582,11 +2605,9 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
span,
NEEDLESS_COLLECT_MSG,
|diag| {
let (arg, pred) = if contains_arg.starts_with('&') {
("x", &contains_arg[1..])
} else {
("&x", &*contains_arg)
};
let (arg, pred) = contains_arg
.strip_prefix('&')
.map_or(("&x", &*contains_arg), |s| ("x", s));
diag.span_suggestion(
span,
"replace with",

View file

@ -122,7 +122,7 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants
fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) {
fn is_private(field: &StructField) -> bool {
matches!(field.vis.node, VisibilityKind::Inherited)
matches!(field.vis.kind, VisibilityKind::Inherited)
}
fn is_non_exhaustive_marker(field: &StructField) -> bool {
@ -141,7 +141,7 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data:
let fields = data.fields();
let private_fields = fields.iter().filter(|f| is_private(f)).count();
let public_fields = fields.iter().filter(|f| f.vis.node.is_pub()).count();
let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count();
if_chain! {
if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1;

View file

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

View file

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

View file

@ -9,7 +9,7 @@ use rustc_span::source_map::Span;
declare_clippy_lint! {
/// **What it does:** Checks for usage of `option.map(f)` where f is a function
/// or closure that returns the unit type.
/// or closure that returns the unit type `()`.
///
/// **Why is this bad?** Readability, this can be written more clearly with
/// an if let statement
@ -51,7 +51,7 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// **What it does:** Checks for usage of `result.map(f)` where f is a function
/// or closure that returns the unit type.
/// or closure that returns the unit type `()`.
///
/// **Why is this bad?** Readability, this can be written more clearly with
/// an if let statement
@ -197,7 +197,7 @@ fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String {
#[must_use]
fn suggestion_msg(function_type: &str, map_type: &str) -> String {
format!(
"called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type",
"called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type `()`",
map_type, function_type
)
}

View file

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

View file

@ -6,7 +6,7 @@ use crate::utils::{
expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet,
snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
span_lint_and_then, walk_ptrs_ty,
span_lint_and_then,
};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
@ -502,7 +502,7 @@ impl_lint_pass!(Matches => [
impl<'tcx> LateLintPass<'tcx> for Matches {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if in_external_macro(cx.sess(), expr.span) {
if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
return;
}
@ -794,7 +794,7 @@ fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms
}
fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
let ex_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(ex));
let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) {
for arm in arms {
if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind {
@ -1440,15 +1440,12 @@ where
mod redundant_pattern_match {
use super::REDUNDANT_PATTERN_MATCHING;
use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
use crate::utils::{match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath};
use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_mir::const_eval::is_const_fn;
use rustc_span::source_map::Symbol;
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
@ -1468,37 +1465,24 @@ mod redundant_pattern_match {
arms: &[Arm<'_>],
keyword: &'static str,
) {
fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> {
if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") {
return Some("is_ok()");
}
if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") {
return Some("is_err()");
}
if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") {
return Some("is_some()");
}
if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") {
return Some("is_none()");
}
None
}
let hir_id = expr.hir_id;
let good_method = match arms[0].pat.kind {
PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => {
if let PatKind::Wild = patterns[0].kind {
find_suggestion(cx, hir_id, path)
if match_qpath(path, &paths::RESULT_OK) {
"is_ok()"
} else if match_qpath(path, &paths::RESULT_ERR) {
"is_err()"
} else if match_qpath(path, &paths::OPTION_SOME) {
"is_some()"
} else {
return;
}
} else {
None
return;
}
},
PatKind::Path(ref path) => find_suggestion(cx, hir_id, path),
_ => None,
};
let good_method = match good_method {
Some(method) => method,
None => return,
PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()",
_ => return,
};
// check that `while_let_on_iterator` lint does not trigger
@ -1547,7 +1531,6 @@ mod redundant_pattern_match {
if arms.len() == 2 {
let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
let hir_id = expr.hir_id;
let found_good_method = match node_pair {
(
PatKind::TupleStruct(ref path_left, ref patterns_left, _),
@ -1562,8 +1545,6 @@ mod redundant_pattern_match {
&paths::RESULT_ERR,
"is_ok()",
"is_err()",
|| can_suggest(cx, hir_id, sym!(result_type), "is_ok"),
|| can_suggest(cx, hir_id, sym!(result_type), "is_err"),
)
} else {
None
@ -1582,8 +1563,6 @@ mod redundant_pattern_match {
&paths::OPTION_NONE,
"is_some()",
"is_none()",
|| can_suggest(cx, hir_id, sym!(option_type), "is_some"),
|| can_suggest(cx, hir_id, sym!(option_type), "is_none"),
)
} else {
None
@ -1616,7 +1595,6 @@ mod redundant_pattern_match {
}
}
#[allow(clippy::too_many_arguments)]
fn find_good_method_for_match<'a>(
arms: &[Arm<'_>],
path_left: &QPath<'_>,
@ -1625,8 +1603,6 @@ mod redundant_pattern_match {
expected_right: &[&str],
should_be_left: &'a str,
should_be_right: &'a str,
can_suggest_left: impl Fn() -> bool,
can_suggest_right: impl Fn() -> bool,
) -> Option<&'a str> {
let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
(&(*arms[0].body).kind, &(*arms[1].body).kind)
@ -1638,35 +1614,13 @@ mod redundant_pattern_match {
match body_node_pair {
(ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
(LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left),
(LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right),
(LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
(LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
_ => None,
},
_ => None,
}
}
fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool {
if !in_constant(cx, hir_id) {
return true;
}
// Avoid suggesting calls to non-`const fn`s in const contexts, see #5697.
cx.tcx
.get_diagnostic_item(diag_item)
.and_then(|def_id| {
cx.tcx.inherent_impls(def_id).iter().find_map(|imp| {
cx.tcx
.associated_items(*imp)
.in_definition_order()
.find_map(|item| match item.kind {
ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id),
_ => None,
})
})
})
.map_or(false, |def_id| is_const_fn(cx.tcx, def_id))
}
}
#[test]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,10 +1,10 @@
use crate::utils::qualify_min_const_fn::is_min_const_fn;
use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method};
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_mir::transform::qualify_min_const_fn::is_min_const_fn;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
use rustc_typeck::hir_ty_to_ty;

View file

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

View file

@ -1,4 +1,4 @@
use crate::utils::span_lint_and_sugg;
use crate::utils::{in_macro, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
use rustc_errors::Applicability;
@ -69,11 +69,30 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
if let [segment] = &path.segments[..];
if segment.ident.name == kw::SelfUpper;
then {
// In case we have a named lifetime, we check if the name comes from expansion.
// If it does, at this point we know the rest of the parameter was written by the user,
// so let them decide what the name of the lifetime should be.
// See #6089 for more details.
let mut applicability = Applicability::MachineApplicable;
let self_param = match (binding_mode, mutbl) {
(Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
(Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name),
(Mode::Ref(Some(lifetime)), Mutability::Mut) => {
if in_macro(lifetime.ident.span) {
applicability = Applicability::HasPlaceholders;
"&'_ mut self".to_string()
} else {
format!("&{} mut self", &lifetime.ident.name)
}
},
(Mode::Ref(None), Mutability::Not) => "&self".to_string(),
(Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name),
(Mode::Ref(Some(lifetime)), Mutability::Not) => {
if in_macro(lifetime.ident.span) {
applicability = Applicability::HasPlaceholders;
"&'_ self".to_string()
} else {
format!("&{} self", &lifetime.ident.name)
}
},
(Mode::Value, Mutability::Mut) => "mut self".to_string(),
(Mode::Value, Mutability::Not) => "self".to_string(),
};
@ -85,7 +104,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
"the type of the `self` parameter does not need to be arbitrary",
"consider to change this parameter to",
self_param,
Applicability::MachineApplicable,
applicability,
)
}
}
@ -93,7 +112,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
impl EarlyLintPass for NeedlessArbitrarySelfType {
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
if !p.is_self() {
// Bail out if the parameter it's not a receiver or was not written by the user
if !p.is_self() || in_macro(p.span) {
return;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -14,7 +14,6 @@ use rustc_middle::mir::{
visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
};
use rustc_middle::ty::{self, fold::TypeVisitor, Ty};
use rustc_mir::dataflow::BottomValue;
use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{BytePos, Span};
@ -88,6 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
let maybe_storage_live_result = MaybeStorageLive
.into_engine(cx.tcx, mir, def_id.to_def_id())
.pass_name("redundant_clone")
.iterate_to_fixpoint()
.into_results_cursor(mir);
let mut possible_borrower = {
@ -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;
}
@ -411,14 +410,15 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
struct MaybeStorageLive;
impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive {
type Idx = mir::Local;
type Domain = BitSet<mir::Local>;
const NAME: &'static str = "maybe_storage_live";
fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
body.local_decls.len()
fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
// bottom = dead
BitSet::new_empty(body.local_decls.len())
}
fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
for arg in body.args_iter() {
state.insert(arg);
}
@ -426,6 +426,8 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive {
}
impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive {
type Idx = mir::Local;
fn statement_effect(&self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, _: mir::Location) {
match stmt.kind {
mir::StatementKind::StorageLive(l) => trans.gen(l),
@ -454,11 +456,6 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive {
}
}
impl BottomValue for MaybeStorageLive {
/// bottom = dead
const BOTTOM_VALUE: bool = false;
}
/// Collects the possible borrowers of each local.
/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
/// possible borrowers of `a`.

View file

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

View file

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

View file

@ -41,7 +41,7 @@ impl EarlyLintPass for SingleComponentPathImports {
if_chain! {
if !in_macro(item.span);
if cx.sess.opts.edition == Edition::Edition2018;
if !item.vis.node.is_pub();
if !item.vis.kind.is_pub();
if let ItemKind::Use(use_tree) = &item.kind;
if let segments = &use_tree.prefix.segments;
if segments.len() == 1;

View file

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

View file

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

View file

@ -1,5 +1,4 @@
use crate::utils::{is_adjusted, span_lint};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -22,12 +21,8 @@ declare_clippy_lint! {
"assignments to temporaries"
}
fn is_temporary(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match &expr.kind {
ExprKind::Struct(..) | ExprKind::Tup(..) => true,
ExprKind::Path(qpath) => matches!(cx.qpath_res(qpath, expr.hir_id), Res::Def(DefKind::Const, ..)),
_ => false,
}
fn is_temporary(expr: &Expr<'_>) -> bool {
matches!(&expr.kind, ExprKind::Struct(..) | ExprKind::Tup(..))
}
declare_lint_pass!(TemporaryAssignment => [TEMPORARY_ASSIGNMENT]);
@ -39,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for TemporaryAssignment {
while let ExprKind::Field(f, _) | ExprKind::Index(f, _) = &base.kind {
base = f;
}
if is_temporary(cx, base) && !is_adjusted(cx, base) {
if is_temporary(base) && !is_adjusted(cx, base) {
span_lint(cx, TEMPORARY_ASSIGNMENT, expr.span, "assignment to temporary");
}
}

View file

@ -11,8 +11,8 @@ use rustc_hir as hir;
use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::{
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn,
TraitItem, TraitItemKind, TyKind, UnOp,
ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind,
TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map;
@ -31,12 +31,13 @@ use crate::utils::paths;
use crate::utils::{
clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item,
last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral,
qpath_res, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability,
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext,
qpath_res, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite,
span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext,
};
declare_clippy_lint! {
/// **What it does:** Checks for use of `Box<Vec<_>>` anywhere in the code.
/// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
///
/// **Why is this bad?** `Vec` already keeps its contents in a separate area on
/// the heap. So if you `Box` it, you just add another level of indirection
@ -65,6 +66,7 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// **What it does:** Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.
/// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
///
/// **Why is this bad?** `Vec` already keeps its contents in a separate area on
/// the heap. So if you `Box` its contents, you just add another level of indirection.
@ -167,6 +169,7 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// **What it does:** Checks for use of `&Box<T>` anywhere in the code.
/// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
///
/// **Why is this bad?** Any `&Box<T>` can also be a `&T`, which is more
/// general.
@ -212,11 +215,42 @@ 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>` usually 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 usually 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:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for
/// cases where mutation only happens before there are any additional references.
///
/// **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) {
@ -269,6 +303,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! {
@ -318,14 +365,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
}
@ -342,14 +390,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
}
@ -365,25 +414,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
@ -543,7 +676,6 @@ impl Types {
// details.
return;
}
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
BORROWED_BOX,
@ -553,8 +685,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
@ -802,6 +938,45 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg {
}
}
fn fmt_stmts_and_call(
cx: &LateContext<'_>,
call_expr: &Expr<'_>,
call_snippet: &str,
args_snippets: &[impl AsRef<str>],
non_empty_block_args_snippets: &[impl AsRef<str>],
) -> String {
let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0);
let call_snippet_with_replacements = args_snippets
.iter()
.fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1));
let mut stmts_and_call = non_empty_block_args_snippets
.iter()
.map(|it| it.as_ref().to_owned())
.collect::<Vec<_>>();
stmts_and_call.push(call_snippet_with_replacements);
stmts_and_call = stmts_and_call
.into_iter()
.map(|v| reindent_multiline(v.into(), true, Some(call_expr_indent)).into_owned())
.collect();
let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent)));
// expr is not in a block statement or result expression position, wrap in a block
let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id));
if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) {
let block_indent = call_expr_indent + 4;
stmts_and_call_snippet =
reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned();
stmts_and_call_snippet = format!(
"{{\n{}{}\n{}}}",
" ".repeat(block_indent),
&stmts_and_call_snippet,
" ".repeat(call_expr_indent)
);
}
stmts_and_call_snippet
}
fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) {
let mut applicability = Applicability::MachineApplicable;
let (singular, plural) = if args_to_recover.len() > 1 {
@ -844,43 +1019,52 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
Applicability::MaybeIncorrect,
);
or = "or ";
applicability = Applicability::MaybeIncorrect;
});
let sugg = args_to_recover
let arg_snippets: Vec<String> = args_to_recover
.iter()
.filter_map(|arg| snippet_opt(cx, arg.span))
.collect();
let arg_snippets_without_empty_blocks: Vec<String> = args_to_recover
.iter()
.filter(|arg| !is_empty_block(arg))
.enumerate()
.map(|(i, arg)| {
let indent = if i == 0 {
0
} else {
indent_of(cx, expr.span).unwrap_or(0)
};
format!(
"{}{};",
" ".repeat(indent),
snippet_block_with_applicability(cx, arg.span, "..", Some(expr.span), &mut applicability)
)
})
.collect::<Vec<String>>();
let mut and = "";
if !sugg.is_empty() {
let plural = if sugg.len() > 1 { "s" } else { "" };
db.span_suggestion(
expr.span.with_hi(expr.span.lo()),
&format!("{}move the expression{} in front of the call...", or, plural),
format!("{}\n", sugg.join("\n")),
applicability,
.filter_map(|arg| snippet_opt(cx, arg.span))
.collect();
if let Some(call_snippet) = snippet_opt(cx, expr.span) {
let sugg = fmt_stmts_and_call(
cx,
expr,
&call_snippet,
&arg_snippets,
&arg_snippets_without_empty_blocks,
);
and = "...and "
if arg_snippets_without_empty_blocks.is_empty() {
db.multipart_suggestion(
&format!("use {}unit literal{} instead", singular, plural),
args_to_recover
.iter()
.map(|arg| (arg.span, "()".to_string()))
.collect::<Vec<_>>(),
applicability,
);
} else {
let plural = arg_snippets_without_empty_blocks.len() > 1;
let empty_or_s = if plural { "s" } else { "" };
let it_or_them = if plural { "them" } else { "it" };
db.span_suggestion(
expr.span,
&format!(
"{}move the expression{} in front of the call and replace {} with the unit literal `()`",
or, empty_or_s, it_or_them
),
sugg,
applicability,
);
}
}
db.multipart_suggestion(
&format!("{}use {}unit literal{} instead", and, singular, plural),
args_to_recover
.iter()
.map(|arg| (arg.span, "()".to_string()))
.collect::<Vec<_>>(),
applicability,
);
},
);
}
@ -2055,6 +2239,7 @@ impl PartialOrd for FullInt {
})
}
}
impl Ord for FullInt {
#[must_use]
fn cmp(&self, other: &Self) -> Ordering {

View file

@ -1,5 +1,4 @@
use crate::utils;
use crate::utils::paths;
use crate::utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_errors::Applicability;
@ -171,12 +170,22 @@ fn mirrored_exprs(
}
fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
// NOTE: Vectors of references are not supported. In order to avoid hitting https://github.com/rust-lang/rust/issues/34162,
// (different unnamed lifetimes for closure arg and return type) we need to make sure the suggested
// closure parameter is not a reference in case we suggest `Reverse`. Trying to destructure more
// than one level of references would add some extra complexity as we would have to compensate
// in the closure body.
if_chain! {
if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind;
if let name = name_ident.ident.name.to_ident_string();
if name == "sort_by" || name == "sort_unstable_by";
if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args;
if utils::match_type(cx, &cx.typeck_results().expr_ty(vec), &paths::VEC);
let vec_ty = cx.typeck_results().expr_ty(vec);
if utils::is_type_diagnostic_item(cx, vec_ty, sym!(vec_type));
let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); // T in Vec<T>
if !matches!(&ty.kind(), ty::Ref(..));
if utils::is_copy(cx, ty);
if let closure_body = cx.tcx.hir().body(*closure_body_id);
if let &[
Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},

View file

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

View file

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

View file

@ -394,7 +394,7 @@ pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
use VisibilityKind::*;
match (&l.node, &r.node) {
match (&l.kind, &r.kind) {
(Public, Public) | (Inherited, Inherited) | (Crate(_), Crate(_)) => true,
(Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r),
_ => false,

View file

@ -164,6 +164,8 @@ define_Conf! {
(max_fn_params_bools, "max_fn_params_bools": u64, 3),
/// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
(warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
/// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses
(disallowed_methods, "disallowed_methods": Vec<String>, Vec::<String>::new()),
}
impl Default for Conf {

View file

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

View file

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

View file

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

View file

@ -10,6 +10,7 @@ pub mod comparisons;
pub mod conf;
pub mod constants;
mod diagnostics;
pub mod eager_or_lazy;
pub mod higher;
mod hir_utils;
pub mod inspector;
@ -17,8 +18,10 @@ pub mod internal_lints;
pub mod numeric_literal;
pub mod paths;
pub mod ptr;
pub mod qualify_min_const_fn;
pub mod sugg;
pub mod usage;
pub use self::attrs::*;
pub use self::diagnostics::*;
pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
@ -44,7 +47,6 @@ use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::map::Map;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
use rustc_mir::const_eval;
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::original_sp;
use rustc_span::symbol::{self, kw, Symbol};
@ -108,6 +110,7 @@ pub fn in_macro(span: Span) -> bool {
false
}
}
// If the snippet is empty, it's an attribute that was inserted during macro
// expansion and we want to ignore those, because they could come from external
// sources that the user has no control over.
@ -128,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),
@ -136,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),
@ -571,7 +579,7 @@ pub fn snippet_block<'a, T: LintContext>(
) -> Cow<'a, str> {
let snip = snippet(cx, span, default);
let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
trim_multiline(snip, true, indent)
reindent_multiline(snip, true, indent)
}
/// Same as `snippet_block`, but adapts the applicability level by the rules of
@ -585,7 +593,7 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>(
) -> Cow<'a, str> {
let snip = snippet_with_applicability(cx, span, default, applicability);
let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
trim_multiline(snip, true, indent)
reindent_multiline(snip, true, indent)
}
/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
@ -661,16 +669,16 @@ pub fn expr_block<'a, T: LintContext>(
}
}
/// Trim indentation from a multiline string with possibility of ignoring the
/// first line.
fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
let s_space = trim_multiline_inner(s, ignore_first, indent, ' ');
let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t');
trim_multiline_inner(s_tab, ignore_first, indent, ' ')
/// Reindent a multiline string with possibility of ignoring the first line.
#[allow(clippy::needless_pass_by_value)]
pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' ');
let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t');
reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into()
}
fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>, ch: char) -> Cow<'_, str> {
let mut x = s
fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
let x = s
.lines()
.skip(ignore_first as usize)
.filter_map(|l| {
@ -683,26 +691,20 @@ fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option<usiz
})
.min()
.unwrap_or(0);
if let Some(indent) = indent {
x = x.saturating_sub(indent);
}
if x > 0 {
Cow::Owned(
s.lines()
.enumerate()
.map(|(i, l)| {
if (ignore_first && i == 0) || l.is_empty() {
l
} else {
l.split_at(x).1
}
})
.collect::<Vec<_>>()
.join("\n"),
)
} else {
s
}
let indent = indent.unwrap_or(0);
s.lines()
.enumerate()
.map(|(i, l)| {
if (ignore_first && i == 0) || l.is_empty() {
l.to_owned()
} else if x > indent {
l.split_at(x - indent).1.to_owned()
} else {
" ".repeat(indent - x) + l
}
})
.collect::<Vec<String>>()
.join("\n")
}
/// Gets the parent expression, if any - this is useful to constrain a lint.
@ -752,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) {
@ -889,19 +883,11 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
/// Checks if an expression is constructing a tuple-like enum variant or struct
pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool {
cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty()
}
if let ExprKind::Call(ref fun, _) = expr.kind {
if let ExprKind::Path(ref qp) = fun.kind {
let res = cx.qpath_res(qp, fun.hir_id);
return match res {
def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
// FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210
def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => {
const_eval::is_const_fn(cx.tcx, def_id)
},
def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
_ => false,
};
@ -1474,26 +1460,26 @@ macro_rules! unwrap_cargo_metadata {
#[cfg(test)]
mod test {
use super::{trim_multiline, without_block_comments};
use super::{reindent_multiline, without_block_comments};
#[test]
fn test_trim_multiline_single_line() {
assert_eq!("", trim_multiline("".into(), false, None));
assert_eq!("...", trim_multiline("...".into(), false, None));
assert_eq!("...", trim_multiline(" ...".into(), false, None));
assert_eq!("...", trim_multiline("\t...".into(), false, None));
assert_eq!("...", trim_multiline("\t\t...".into(), false, None));
fn test_reindent_multiline_single_line() {
assert_eq!("", reindent_multiline("".into(), false, None));
assert_eq!("...", reindent_multiline("...".into(), false, None));
assert_eq!("...", reindent_multiline(" ...".into(), false, None));
assert_eq!("...", reindent_multiline("\t...".into(), false, None));
assert_eq!("...", reindent_multiline("\t\t...".into(), false, None));
}
#[test]
#[rustfmt::skip]
fn test_trim_multiline_block() {
fn test_reindent_multiline_block() {
assert_eq!("\
if x {
y
} else {
z
}", trim_multiline(" if x {
}", reindent_multiline(" if x {
y
} else {
z
@ -1503,7 +1489,7 @@ mod test {
\ty
} else {
\tz
}", trim_multiline(" if x {
}", reindent_multiline(" if x {
\ty
} else {
\tz
@ -1512,14 +1498,14 @@ mod test {
#[test]
#[rustfmt::skip]
fn test_trim_multiline_empty_line() {
fn test_reindent_multiline_empty_line() {
assert_eq!("\
if x {
y
} else {
z
}", trim_multiline(" if x {
}", reindent_multiline(" if x {
y
} else {
@ -1527,6 +1513,22 @@ mod test {
}".into(), false, None));
}
#[test]
#[rustfmt::skip]
fn test_reindent_multiline_lines_deeper() {
assert_eq!("\
if x {
y
} else {
z
}", reindent_multiline("\
if x {
y
} else {
z
}".into(), true, Some(8)));
}
#[test]
fn test_without_block_comments_lines_without_block_comments() {
let result = without_block_comments(vec!["/*", "", "*/"]);

View file

@ -109,14 +109,19 @@ pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWrite
pub const SERDE_DESERIALIZE: [&str; 2] = ["_serde", "Deserialize"];
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"];
pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"];
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
pub const STRING: [&str; 3] = ["alloc", "string", "String"];
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];

View file

@ -0,0 +1,347 @@
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
TerminatorKind,
};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi::RustIntrinsic;
use std::borrow::Cow;
type McfResult = Result<(), (Span, Cow<'static, str>)>;
pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -> McfResult {
let mut current = def_id;
loop {
let predicates = tcx.predicates_of(current);
for (predicate, _) in predicates.predicates {
match predicate.skip_binders() {
ty::PredicateAtom::RegionOutlives(_)
| ty::PredicateAtom::TypeOutlives(_)
| ty::PredicateAtom::WellFormed(_)
| ty::PredicateAtom::Projection(_)
| ty::PredicateAtom::ConstEvaluatable(..)
| ty::PredicateAtom::ConstEquate(..)
| ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue,
ty::PredicateAtom::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
ty::PredicateAtom::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
ty::PredicateAtom::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
ty::PredicateAtom::Trait(pred, _) => {
if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
continue;
}
match pred.self_ty().kind() {
ty::Param(ref p) => {
let generics = tcx.generics_of(current);
let def = generics.type_param(p, tcx);
let span = tcx.def_span(def.def_id);
return Err((
span,
"trait bounds other than `Sized` \
on const fn parameters are unstable"
.into(),
));
},
// other kinds of bounds are either tautologies
// or cause errors in other passes
_ => continue,
}
},
}
}
match predicates.parent {
Some(parent) => current = parent,
None => break,
}
}
for local in &body.local_decls {
check_ty(tcx, local.ty, local.source_info.span)?;
}
// impl trait is gone in MIR, so check the return type manually
check_ty(
tcx,
tcx.fn_sig(def_id).output().skip_binder(),
body.local_decls.iter().next().unwrap().source_info.span,
)?;
for bb in body.basic_blocks() {
check_terminator(tcx, body, bb.terminator())?;
for stmt in &bb.statements {
check_statement(tcx, body, def_id, stmt)?;
}
}
Ok(())
}
fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
for arg in ty.walk() {
let ty = match arg.unpack() {
GenericArgKind::Type(ty) => ty,
// No constraints on lifetimes or constants, except potentially
// constants' types, but `walk` will get to them as well.
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
};
match ty.kind() {
ty::Ref(_, _, hir::Mutability::Mut) => {
return Err((span, "mutable references in const fn are unstable".into()));
},
ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
ty::FnPtr(..) => {
return Err((span, "function pointers in const fn are unstable".into()));
},
ty::Dynamic(preds, _) => {
for pred in preds.iter() {
match pred.skip_binder() {
ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
return Err((
span,
"trait bounds other than `Sized` \
on const fn parameters are unstable"
.into(),
));
},
ty::ExistentialPredicate::Trait(trait_ref) => {
if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
return Err((
span,
"trait bounds other than `Sized` \
on const fn parameters are unstable"
.into(),
));
}
},
}
}
},
_ => {},
}
}
Ok(())
}
fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
match rvalue {
Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
check_place(tcx, *place, span, body)
},
Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
use rustc_middle::ty::cast::CastTy;
let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
match (cast_in, cast_out) {
(CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
Err((span, "casting pointers to ints is unstable in const fn".into()))
},
_ => check_operand(tcx, operand, span, body),
}
},
Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
check_operand(tcx, operand, span, body)
},
Rvalue::Cast(
CastKind::Pointer(
PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
),
_,
_,
) => Err((span, "function pointer casts are not allowed in const fn".into())),
Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
deref_ty.ty
} else {
// We cannot allow this for now.
return Err((span, "unsizing casts are only allowed for references right now".into()));
};
let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
check_operand(tcx, op, span, body)?;
// Casting/coercing things to slices is fine.
Ok(())
} else {
// We just can't allow trait objects until we have figured out trait method calls.
Err((span, "unsizing casts are not allowed in const fn".into()))
}
},
// binops are fine on integers
Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
check_operand(tcx, lhs, span, body)?;
check_operand(tcx, rhs, span, body)?;
let ty = lhs.ty(body, tcx);
if ty.is_integral() || ty.is_bool() || ty.is_char() {
Ok(())
} else {
Err((
span,
"only int, `bool` and `char` operations are stable in const fn".into(),
))
}
},
Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, tcx);
if ty.is_integral() || ty.is_bool() {
check_operand(tcx, operand, span, body)
} else {
Err((span, "only int and `bool` operations are stable in const fn".into()))
}
},
Rvalue::Aggregate(_, operands) => {
for operand in operands {
check_operand(tcx, operand, span, body)?;
}
Ok(())
},
}
}
fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
let span = statement.source_info.span;
match &statement.kind {
StatementKind::Assign(box (place, rval)) => {
check_place(tcx, *place, span, body)?;
check_rvalue(tcx, body, def_id, rval, span)
},
StatementKind::FakeRead(_, place) |
// just an assignment
StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),
StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
// These are all NOPs
StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Retag { .. }
| StatementKind::AscribeUserType(..)
| StatementKind::Coverage(..)
| StatementKind::Nop => Ok(()),
}
}
fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
match operand {
Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
Operand::Constant(c) => match c.check_static_ptr(tcx) {
Some(_) => Err((span, "cannot access `static` items in const fn".into())),
None => Ok(()),
},
}
}
fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
let mut cursor = place.projection.as_ref();
while let [ref proj_base @ .., elem] = *cursor {
cursor = proj_base;
match elem {
ProjectionElem::Field(..) => {
let base_ty = Place::ty_from(place.local, &proj_base, body, tcx).ty;
if let Some(def) = base_ty.ty_adt_def() {
// No union field accesses in `const fn`
if def.is_union() {
return Err((span, "accessing union fields is unstable".into()));
}
}
},
ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. }
| ProjectionElem::Deref
| ProjectionElem::Index(_) => {},
}
}
Ok(())
}
fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
let span = terminator.source_info.span;
match &terminator.kind {
TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Goto { .. }
| TerminatorKind::Return
| TerminatorKind::Resume
| TerminatorKind::Unreachable => Ok(()),
TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
TerminatorKind::DropAndReplace { place, value, .. } => {
check_place(tcx, *place, span, body)?;
check_operand(tcx, value, span, body)
},
TerminatorKind::SwitchInt {
discr,
switch_ty: _,
values: _,
targets: _,
} => check_operand(tcx, discr, span, body),
TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
Err((span, "const fn generators are unstable".into()))
},
TerminatorKind::Call {
func,
args,
from_hir_call: _,
destination: _,
cleanup: _,
fn_span: _,
} => {
let fn_ty = func.ty(body, tcx);
if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
return Err((
span,
format!(
"can only call other `const fn` within a `const fn`, \
but `{:?}` is not stable as `const fn`",
func,
)
.into(),
));
}
// HACK: This is to "unstabilize" the `transmute` intrinsic
// within const fns. `transmute` is allowed in all other const contexts.
// This won't really scale to more intrinsics or functions. Let's allow const
// transmutes in const fn before we add more hacks to this.
if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
return Err((
span,
"can only call `transmute` from const items, not `const fn`".into(),
));
}
check_operand(tcx, func, span, body)?;
for arg in args {
check_operand(tcx, arg, span, body)?;
}
Ok(())
} else {
Err((span, "can only call other const fns within const fn".into()))
}
},
TerminatorKind::Assert {
cond,
expected: _,
msg: _,
target: _,
cleanup: _,
} => check_operand(tcx, cond, span, body),
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
}
}

View file

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

View file

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

View file

@ -235,8 +235,19 @@ impl EarlyLintPass for Write {
}
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
fn is_build_script(cx: &EarlyContext<'_>) -> bool {
// Cargo sets the crate name for build scripts to `build_script_build`
cx.sess
.opts
.crate_name
.as_ref()
.map_or(false, |crate_name| crate_name == "build_script_build")
}
if mac.path == sym!(println) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
if !is_build_script(cx) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
}
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
if fmt_str.symbol == Symbol::intern("") {
span_lint_and_sugg(
@ -251,7 +262,9 @@ impl EarlyLintPass for Write {
}
}
} else if mac.path == sym!(print) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
if !is_build_script(cx) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
}
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
if check_newlines(&fmt_str) {
span_lint_and_then(
@ -322,11 +335,15 @@ impl EarlyLintPass for Write {
}
/// Given a format string that ends in a newline and its span, calculates the span of the
/// newline.
/// newline, or the format string itself if the format string consists solely of a newline.
fn newline_span(fmtstr: &StrLit) -> Span {
let sp = fmtstr.span;
let contents = &fmtstr.symbol.as_str();
if *contents == r"\n" {
return sp;
}
let newline_sp_hi = sp.hi()
- match fmtstr.style {
StrStyle::Cooked => BytePos(1),

View file

@ -0,0 +1,7 @@
#![deny(clippy::print_stdout)]
fn main() {
// Test for #6041
println!("Hello");
print!("Hello");
}

View file

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

View file

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

View file

@ -357,7 +357,7 @@ pub fn main() {
args.extend(vec!["--sysroot".into(), sys_root]);
};
return rustc_driver::run_compiler(&args, &mut DefaultCallbacks, None, None);
return rustc_driver::run_compiler(&args, &mut DefaultCallbacks, None, None, None);
}
if orig_args.iter().any(|a| a == "--version" || a == "-V") {
@ -420,6 +420,6 @@ pub fn main() {
let mut default = DefaultCallbacks;
let callbacks: &mut (dyn rustc_driver::Callbacks + Send) =
if clippy_enabled { &mut clippy } else { &mut default };
rustc_driver::run_compiler(&args, callbacks, None, None)
rustc_driver::run_compiler(&args, callbacks, None, None, None)
}))
}

View file

@ -297,6 +297,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "copy_iterator",
},
Lint {
name: "create_dir",
group: "restriction",
desc: "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`",
deprecation: None,
module: "create_dir",
},
Lint {
name: "crosspointer_transmute",
group: "complexity",
@ -374,6 +381,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "derive",
},
Lint {
name: "disallowed_method",
group: "nursery",
desc: "use of a disallowed method call",
deprecation: None,
module: "disallowed_method",
},
Lint {
name: "diverging_sub_expression",
group: "complexity",
@ -923,7 +937,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",
},
@ -1137,6 +1151,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",
@ -1165,6 +1186,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",
@ -1711,6 +1739,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",
@ -1837,6 +1872,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",
@ -2623,7 +2665,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
},
Lint {
name: "verbose_bit_mask",
group: "style",
group: "pedantic",
desc: "expressions where a bit mask is less readable than the corresponding method call",
deprecation: None,
module: "bit_mask",

View file

@ -71,7 +71,7 @@ fn default_config() -> compiletest::Config {
}
config.target_rustcflags = Some(format!(
"-L {0} -L {1} -Dwarnings -Zui-testing {2}",
"--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}",
host_lib().join("deps").display(),
cargo::TARGET_LIB.join("deps").display(),
third_party_crates(),

View file

@ -0,0 +1 @@
disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match"]

View file

@ -0,0 +1,13 @@
#![warn(clippy::disallowed_method)]
extern crate regex;
use regex::Regex;
fn main() {
let a = vec![1, 2, 3, 4];
let re = Regex::new(r"ab.*c").unwrap();
re.is_match("abc");
a.iter().sum::<i32>();
}

View file

@ -0,0 +1,16 @@
error: use of a disallowed method `regex::re_unicode::Regex::is_match`
--> $DIR/conf_disallowed_method.rs:10:5
|
LL | re.is_match("abc");
| ^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::disallowed-method` implied by `-D warnings`
error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum`
--> $DIR/conf_disallowed_method.rs:12:5
|
LL | a.iter().sum::<i32>();
| ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View file

@ -1,4 +1,4 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1
error: aborting due to previous error

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,8 @@
// compile-flags: --emit=link
// no-prefer-dynamic
#![crate_type = "proc-macro"]
#![feature(repr128, proc_macro_hygiene, proc_macro_quote)]
#![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)]
#![allow(clippy::useless_conversion)]
extern crate proc_macro;
@ -11,7 +12,11 @@ extern crate syn;
use proc_macro::TokenStream;
use quote::{quote, quote_spanned};
use syn::parse_macro_input;
use syn::{parse_quote, ItemTrait, TraitItem};
use syn::spanned::Spanned;
use syn::token::Star;
use syn::{
parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type,
};
#[proc_macro_attribute]
pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
@ -35,3 +40,56 @@ pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
}
TokenStream::from(quote!(#item))
}
#[proc_macro_attribute]
pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStream {
fn make_name(count: usize) -> String {
format!("'life{}", count)
}
fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> {
let arg = sig.inputs.first_mut()?;
if let FnArg::Typed(PatType { pat, .. }) = arg {
if let Pat::Ident(PatIdent { ident, .. }) = &**pat {
if ident == "self" {
return Some(arg);
}
}
}
None
}
let mut elided = 0;
let mut item = parse_macro_input!(input as ItemImpl);
// Look for methods having arbitrary self type taken by &mut ref
for inner in &mut item.items {
if let ImplItem::Method(method) = inner {
if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) {
if let box Type::Reference(reference) = &mut pat_type.ty {
// Target only unnamed lifetimes
let name = match &reference.lifetime {
Some(lt) if lt.ident == "_" => make_name(elided),
None => make_name(elided),
_ => continue,
};
elided += 1;
// HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it.
// In order to avoid adding the dependency, get a default span from a non-existent token.
// A default span is needed to mark the code as coming from expansion.
let span = Star::default().span();
// Replace old lifetime with the named one
let lifetime = Lifetime::new(&name, span);
reference.lifetime = Some(parse_quote!(#lifetime));
// Add lifetime to the generics of the method
method.sig.generics.params.push(parse_quote!(#lifetime));
}
}
}
}
TokenStream::from(quote!(#item))
}

View file

@ -1,3 +1,4 @@
// compile-flags: --emit=link
// no-prefer-dynamic
#![crate_type = "proc-macro"]

View file

@ -1,5 +1,6 @@
#![warn(clippy::borrow_interior_mutable_const)]
#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)]
#![allow(const_item_mutation)]
use std::borrow::Cow;
use std::cell::{Cell, UnsafeCell};
@ -18,16 +19,30 @@ const NO_ANN: &dyn Display = &70;
static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
const ONCE_INIT: Once = Once::new();
trait Trait<T>: Copy {
type NonCopyType;
trait Trait<T> {
type AssocType;
const ATOMIC: AtomicUsize;
const INPUT: T;
const ASSOC: Self::AssocType;
fn function() {
let _ = &Self::INPUT;
let _ = &Self::ASSOC;
}
}
impl Trait<u32> for u64 {
type NonCopyType = u16;
type AssocType = AtomicUsize;
const ATOMIC: AtomicUsize = AtomicUsize::new(9);
const INPUT: u32 = 10;
const ASSOC: Self::AssocType = AtomicUsize::new(11);
fn function() {
let _ = &Self::INPUT;
let _ = &Self::ASSOC; //~ ERROR interior mutability
}
}
// This is just a pointer that can be safely dereferended,

View file

@ -1,14 +1,22 @@
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:65: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:66: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:69: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:70: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:71: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:72: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:83: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:84: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:85: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:86: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:87: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:93: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:98: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:99: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:112: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:113:16
--> $DIR/borrow_interior_mutable_const.rs:128:16
|
LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
| ^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
error: aborting due to 16 previous errors
error: aborting due to 17 previous errors

View file

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/1698
pub trait Trait {

View file

@ -1,3 +1,4 @@
// compile-flags: --emit=link
// no-prefer-dynamic
// ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro
// crates. If we don't set this, compiletest will override the `crate_type` attribute below and

View file

@ -1,5 +1,3 @@
// run-pass
#[allow(dead_code)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/478

View file

@ -1,5 +1,3 @@
// run-pass
#![deny(clippy::all)]
#![allow(unused_imports)]

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(clippy::all)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/1588

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(dead_code, unused_variables)]
/// Should not trigger an ICE in `SpanlessEq` / `consts::constant`

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(clippy::all)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/1969

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)]
/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(dead_code, unused_variables)]
/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`

View file

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

View file

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

View file

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/2727
pub fn f(new: fn()) {

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(
unused_variables,
clippy::blacklisted_name,

View file

@ -1,5 +1,3 @@
// run-pass
use std::collections::HashSet;
// See rust-lang/rust-clippy#2774.

View file

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/2862
pub trait FooMap {

View file

@ -1,5 +1,3 @@
// run-pass
#[allow(dead_code)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/2865

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