Auto merge of #6247 - giraffate:fix_suggestion_to_add_space_in_unused_unit, r=ebroto
Fix suggestion to add unneeded space in `unused_unit`
Fix https://github.com/rust-lang/rust-clippy/issues/6230
changelog: Fix suggestion to add unneeded space in `unused_unit`
diff --git a/.github/driver.sh b/.github/driver.sh
index 2c17c42..6ff189f 100644
--- a/.github/driver.sh
+++ b/.github/driver.sh
@@ -7,9 +7,9 @@
test "$sysroot" = "$(rustc --print sysroot)"
if [[ ${OS} == "Windows" ]]; then
- desired_sysroot=C:/tmp
+ desired_sysroot=C:/tmp
else
- desired_sysroot=/tmp
+ desired_sysroot=/tmp
fi
sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot)
test "$sysroot" = $desired_sysroot
@@ -22,20 +22,18 @@
# Run a lint and make sure it produces the expected output. It's also expected to exit with code 1
# FIXME: How to match the clippy invocation in compile-test.rs?
-./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/cstring.rs 2> cstring.stderr && exit 1
-sed -e "s,tests/ui,\$DIR," -e "/= help/d" cstring.stderr > normalized.stderr
-diff normalized.stderr tests/ui/cstring.stderr
-
+./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2>double_neg.stderr && exit 1
+sed -e "s,tests/ui,\$DIR," -e "/= help/d" double_neg.stderr >normalized.stderr
+diff -u normalized.stderr tests/ui/double_neg.stderr
# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same
-SYSROOT=`rustc --print sysroot`
-diff <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose)
+SYSROOT=$(rustc --print sysroot)
+diff -u <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose)
-
-echo "fn main() {}" > target/driver_test.rs
+echo "fn main() {}" >target/driver_test.rs
# we can't run 2 rustcs on the same file at the same time
-CLIPPY=`LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc`
-RUSTC=`rustc ./target/driver_test.rs`
-diff <($CLIPPY) <($RUSTC)
+CLIPPY=$(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc)
+RUSTC=$(rustc ./target/driver_test.rs)
+diff -u <($CLIPPY) <($RUSTC)
# TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7fd79de..1b9b338 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,7 +22,7 @@
* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
* [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
-* [`single_char_push_str`] [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
+* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
### Moves and Deprecations
@@ -1665,6 +1665,7 @@
[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
[`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
+[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
[`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
@@ -1713,6 +1714,7 @@
[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
+[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
@@ -1731,6 +1733,7 @@
[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
+[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
@@ -1795,6 +1798,7 @@
[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
[`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_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
[`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
@@ -1802,6 +1806,7 @@
[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
[`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_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
[`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
@@ -1917,6 +1922,7 @@
[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
+[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
@@ -1937,8 +1943,8 @@
[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
+[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
-[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str
[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
@@ -2017,6 +2023,7 @@
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
+[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6494695..a8e2123 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -63,9 +63,10 @@
Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting].
But we can make it nest-less by using [if_chain] macro, [like this][nest-less].
-[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an E-easy issue first.
-They are mostly classified as [`E-medium`], since they might be somewhat involved code wise,
-but not difficult per-se.
+[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`]
+first. Sometimes they are only somewhat involved code wise, but not difficult per-se.
+Note that [`E-medium`] issues may require some knowledge of Clippy internals or some
+debugging to find the actual problem behind the issue.
[`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
diff --git a/README.md b/README.md
index e1b3c84..8a5975e 100644
--- a/README.md
+++ b/README.md
@@ -167,18 +167,21 @@
* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
-Note: `deny` produces errors instead of warnings.
+Note: `allow` means to suppress the lint for your code. With `warn` the lint
+will only emit a warning, while with `deny` the lint will emit an error, when
+triggering for your code. An error causes clippy to exit with an error code, so
+is useful in scripts like CI/CD.
-If you do not want to include your lint levels in your code, you can globally enable/disable lints
-by passing extra flags to Clippy during the run:
+If you do not want to include your lint levels in your code, you can globally
+enable/disable lints by passing extra flags to Clippy during the run:
-To disable `lint_name`, run
+To allow `lint_name`, run
```terminal
cargo clippy -- -A clippy::lint_name
```
-And to enable `lint_name`, run
+And to warn on `lint_name`, run
```terminal
cargo clippy -- -W clippy::lint_name
@@ -190,7 +193,7 @@
cargo clippy -- -W clippy::pedantic
```
-If you care only about a single lint, you can allow all others and then explicitly reenable
+If you care only about a single lint, you can allow all others and then explicitly warn on
the lint(s) you are interested in:
```terminal
cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
diff --git a/clippy_dev/src/ra_setup.rs b/clippy_dev/src/ra_setup.rs
index c67efc1..9d9e836 100644
--- a/clippy_dev/src/ra_setup.rs
+++ b/clippy_dev/src/ra_setup.rs
@@ -11,7 +11,7 @@
// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details
pub fn run(rustc_path: Option<&str>) {
- // we can unwrap here because the arg is required here
+ // we can unwrap here because the arg is required by clap
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("compiler");
@@ -49,6 +49,15 @@
cargo_toml: &str,
lib_rs: &str,
) -> std::io::Result<()> {
+ // do not inject deps if we have aleady done so
+ if cargo_toml.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") {
+ eprintln!(
+ "cargo dev ra-setup: warning: deps already found inside {}, doing nothing.",
+ manifest_path
+ );
+ return Ok(());
+ }
+
let extern_crates = lib_rs
.lines()
// get the deps
diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/arithmetic.rs
index e84481f..9861d8c 100644
--- a/clippy_lints/src/arithmetic.rs
+++ b/clippy_lints/src/arithmetic.rs
@@ -88,9 +88,28 @@
let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r));
if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
- span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
- self.expr_span = Some(expr.span);
- } else if l_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
+ match op.node {
+ hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind {
+ hir::ExprKind::Lit(_lit) => (),
+ hir::ExprKind::Unary(hir::UnOp::UnNeg, expr) => {
+ if let hir::ExprKind::Lit(lit) = &expr.kind {
+ if let rustc_ast::ast::LitKind::Int(1, _) = lit.node {
+ span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+ self.expr_span = Some(expr.span);
+ }
+ }
+ },
+ _ => {
+ span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+ self.expr_span = Some(expr.span);
+ },
+ },
+ _ => {
+ span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+ self.expr_span = Some(expr.span);
+ },
+ }
+ } else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
self.expr_span = Some(expr.span);
}
diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs
index f6eadbd..a004abb 100644
--- a/clippy_lints/src/attrs.rs
+++ b/clippy_lints/src/attrs.rs
@@ -16,6 +16,7 @@
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
+use rustc_span::sym;
use rustc_span::symbol::{Symbol, SymbolStr};
use semver::Version;
@@ -286,14 +287,14 @@
},
_ => {},
}
- if items.is_empty() || !attr.has_name(sym!(deprecated)) {
+ if items.is_empty() || !attr.has_name(sym::deprecated) {
return;
}
for item in items {
if_chain! {
if let NestedMetaItem::MetaItem(mi) = &item;
if let MetaItemKind::NameValue(lit) = &mi.kind;
- if mi.has_name(sym!(since));
+ if mi.has_name(sym::since);
then {
check_semver(cx, item.span(), lit);
}
@@ -309,7 +310,7 @@
}
match item.kind {
ItemKind::ExternCrate(..) | ItemKind::Use(..) => {
- let skip_unused_imports = item.attrs.iter().any(|attr| attr.has_name(sym!(macro_use)));
+ let skip_unused_imports = item.attrs.iter().any(|attr| attr.has_name(sym::macro_use));
for attr in item.attrs {
if in_external_macro(cx.sess(), attr.span) {
@@ -326,7 +327,7 @@
match item.kind {
ItemKind::Use(..) => {
if is_word(lint, sym!(unused_imports))
- || is_word(lint, sym!(deprecated))
+ || is_word(lint, sym::deprecated)
|| is_word(lint, sym!(unreachable_pub))
|| is_word(lint, sym!(unused))
|| extract_clippy_lint(lint)
@@ -411,8 +412,7 @@
let lint_store = cx.lints();
for lint in items {
if let Some(lint_name) = extract_clippy_lint(lint) {
- if let CheckLintNameResult::Tool(Err((None, _))) =
- lint_store.check_lint_name(&lint_name, Some(sym!(clippy)))
+ if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(&lint_name, Some(sym::clippy))
{
span_lint_and_then(
cx,
@@ -529,10 +529,10 @@
for attr in attrs {
if let Some(values) = attr.meta_item_list() {
- if values.len() != 1 || !attr.has_name(sym!(inline)) {
+ if values.len() != 1 || !attr.has_name(sym::inline) {
continue;
}
- if is_word(&values[0], sym!(always)) {
+ if is_word(&values[0], sym::always) {
span_lint(
cx,
INLINE_ALWAYS,
@@ -623,12 +623,12 @@
fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
if_chain! {
// check cfg_attr
- if attr.has_name(sym!(cfg_attr));
+ if attr.has_name(sym::cfg_attr);
if let Some(items) = attr.meta_item_list();
if items.len() == 2;
// check for `rustfmt`
if let Some(feature_item) = items[0].meta_item();
- if feature_item.has_name(sym!(rustfmt));
+ if feature_item.has_name(sym::rustfmt);
// check for `rustfmt_skip` and `rustfmt::skip`
if let Some(skip_item) = &items[1].meta_item();
if skip_item.has_name(sym!(rustfmt_skip)) ||
@@ -690,7 +690,7 @@
}
if_chain! {
- if attr.has_name(sym!(cfg));
+ if attr.has_name(sym::cfg);
if let Some(list) = attr.meta_item_list();
let mismatched = find_mismatched_target_os(&list);
if !mismatched.is_empty();
diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs
index 1b73ced..736730d 100644
--- a/clippy_lints/src/blocks_in_if_conditions.rs
+++ b/clippy_lints/src/blocks_in_if_conditions.rs
@@ -28,7 +28,6 @@
///
/// ```rust
/// # fn somefunc() -> bool { true };
- ///
/// // Bad
/// if { let x = somefunc(); x } { /* ... */ }
///
diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs
index 280a2c7..90bb0bd 100644
--- a/clippy_lints/src/booleans.rs
+++ b/clippy_lints/src/booleans.rs
@@ -11,6 +11,7 @@
use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for boolean expressions that can be written more
@@ -253,8 +254,8 @@
},
ExprKind::MethodCall(path, _, args, _) if args.len() == 1 => {
let type_of_receiver = cx.typeck_results().expr_ty(&args[0]);
- if !is_type_diagnostic_item(cx, type_of_receiver, sym!(option_type))
- && !is_type_diagnostic_item(cx, type_of_receiver, sym!(result_type))
+ if !is_type_diagnostic_item(cx, type_of_receiver, sym::option_type)
+ && !is_type_diagnostic_item(cx, type_of_receiver, sym::result_type)
{
return None;
}
diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs
index d7d02eb..38a0e27 100644
--- a/clippy_lints/src/bytecount.rs
+++ b/clippy_lints/src/bytecount.rs
@@ -8,6 +8,7 @@
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
use rustc_span::Symbol;
declare_clippy_lint! {
@@ -68,7 +69,7 @@
let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) =
filter_args[0].kind {
let p = path.ident.name;
- if (p == sym!(iter) || p == sym!(iter_mut)) && args.len() == 1 {
+ if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
&args[0]
} else {
&filter_args[0]
diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs
index 14ef8c3..b1bc2ec 100644
--- a/clippy_lints/src/cognitive_complexity.rs
+++ b/clippy_lints/src/cognitive_complexity.rs
@@ -7,7 +7,7 @@
use rustc_middle::hir::map::Map;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
-use rustc_span::BytePos;
+use rustc_span::{sym, BytePos};
use crate::utils::{is_type_diagnostic_item, snippet_opt, span_lint_and_help, LimitStack};
@@ -61,7 +61,7 @@
helper.visit_expr(expr);
let CCHelper { cc, returns } = helper;
let ret_ty = cx.typeck_results().node_type(expr.hir_id);
- let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym!(result_type)) {
+ let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::result_type) {
returns
} else {
#[allow(clippy::integer_division)]
@@ -123,7 +123,7 @@
hir_id: HirId,
) {
let def_id = cx.tcx.hir().local_def_id(hir_id);
- if !cx.tcx.has_attr(def_id.to_def_id(), sym!(test)) {
+ if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) {
self.check(cx, kind, decl, body, span);
}
}
diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs
index c5e33b2..0035ded 100644
--- a/clippy_lints/src/consts.rs
+++ b/clippy_lints/src/consts.rs
@@ -7,8 +7,9 @@
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
use rustc_lint::LateContext;
+use rustc_middle::mir::interpret::Scalar;
use rustc_middle::ty::subst::{Subst, SubstsRef};
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::symbol::Symbol;
use std::cmp::Ordering::{self, Equal};
@@ -500,21 +501,21 @@
}
pub fn miri_to_const(result: &ty::Const<'_>) -> Option<Constant> {
- use rustc_middle::mir::interpret::{ConstValue, Scalar};
+ use rustc_middle::mir::interpret::ConstValue;
match result.val {
- ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data: d, .. })) => {
+ ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => {
match result.ty.kind() {
- ty::Bool => Some(Constant::Bool(d == 1)),
- ty::Uint(_) | ty::Int(_) => Some(Constant::Int(d)),
+ ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
+ ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
- d.try_into().expect("invalid f32 bit representation"),
+ int.try_into().expect("invalid f32 bit representation"),
))),
ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
- d.try_into().expect("invalid f64 bit representation"),
+ int.try_into().expect("invalid f64 bit representation"),
))),
ty::RawPtr(type_and_mut) => {
if let ty::Uint(_) = type_and_mut.ty.kind() {
- return Some(Constant::RawPtr(d));
+ return Some(Constant::RawPtr(int.assert_bits(int.size())));
}
None
},
diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs
index 6c969c3..46ce92e 100644
--- a/clippy_lints/src/copies.rs
+++ b/clippy_lints/src/copies.rs
@@ -1,13 +1,8 @@
-use crate::utils::{eq_expr_value, in_macro, SpanlessEq, SpanlessHash};
-use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind};
+use crate::utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash};
+use crate::utils::{get_parent_expr, higher, if_sequence, span_lint_and_note};
+use rustc_hir::{Block, Expr};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{Ty, TyS};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::Symbol;
-use std::collections::hash_map::Entry;
-use std::hash::BuildHasherDefault;
declare_clippy_lint! {
/// **What it does:** Checks for consecutive `if`s with the same condition.
@@ -108,48 +103,7 @@
"`if` with the same `then` and `else` blocks"
}
-declare_clippy_lint! {
- /// **What it does:** Checks for `match` with identical arm bodies.
- ///
- /// **Why is this bad?** This is probably a copy & paste error. If arm bodies
- /// are the same on purpose, you can factor them
- /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
- ///
- /// **Known problems:** False positive possible with order dependent `match`
- /// (see issue
- /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
- ///
- /// **Example:**
- /// ```rust,ignore
- /// match foo {
- /// Bar => bar(),
- /// Quz => quz(),
- /// Baz => bar(), // <= oops
- /// }
- /// ```
- ///
- /// This should probably be
- /// ```rust,ignore
- /// match foo {
- /// Bar => bar(),
- /// Quz => quz(),
- /// Baz => baz(), // <= fixed
- /// }
- /// ```
- ///
- /// or if the original code was not a typo:
- /// ```rust,ignore
- /// match foo {
- /// Bar | Baz => bar(), // <= shows the intent better
- /// Quz => quz(),
- /// }
- /// ```
- pub MATCH_SAME_ARMS,
- pedantic,
- "`match` with identical arm bodies"
-}
-
-declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]);
+declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE]);
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
@@ -167,7 +121,6 @@
lint_same_then_else(cx, &blocks);
lint_same_cond(cx, &conds);
lint_same_fns_in_if_cond(cx, &conds);
- lint_match_arms(cx, expr);
}
}
}
@@ -243,122 +196,6 @@
}
}
-/// Implementation of `MATCH_SAME_ARMS`.
-fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
- fn same_bindings<'tcx>(lhs: &FxHashMap<Symbol, Ty<'tcx>>, rhs: &FxHashMap<Symbol, Ty<'tcx>>) -> bool {
- lhs.len() == rhs.len()
- && lhs
- .iter()
- .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty)))
- }
-
- if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind {
- let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
- let mut h = SpanlessHash::new(cx);
- h.hash_expr(&arm.body);
- h.finish()
- };
-
- let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
- let min_index = usize::min(lindex, rindex);
- let max_index = usize::max(lindex, rindex);
-
- // Arms with a guard are ignored, those can’t always be merged together
- // This is also the case for arms in-between each there is an arm with a guard
- (min_index..=max_index).all(|index| arms[index].guard.is_none()) &&
- SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) &&
- // all patterns should have the same bindings
- same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat))
- };
-
- let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
- for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
- span_lint_and_then(
- cx,
- MATCH_SAME_ARMS,
- j.body.span,
- "this `match` has identical arm bodies",
- |diag| {
- diag.span_note(i.body.span, "same as this");
-
- // Note: this does not use `span_suggestion` on purpose:
- // there is no clean way
- // to remove the other arm. Building a span and suggest to replace it to ""
- // makes an even more confusing error message. Also in order not to make up a
- // span for the whole pattern, the suggestion is only shown when there is only
- // one pattern. The user should know about `|` if they are already using it…
-
- let lhs = snippet(cx, i.pat.span, "<pat1>");
- let rhs = snippet(cx, j.pat.span, "<pat2>");
-
- if let PatKind::Wild = j.pat.kind {
- // if the last arm is _, then i could be integrated into _
- // note that i.pat cannot be _, because that would mean that we're
- // hiding all the subsequent arms, and rust won't compile
- diag.span_note(
- i.body.span,
- &format!(
- "`{}` has the same arm body as the `_` wildcard, consider removing it",
- lhs
- ),
- );
- } else {
- diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
- }
- },
- );
- }
- }
-}
-
-/// Returns the list of bindings in a pattern.
-fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap<Symbol, Ty<'tcx>> {
- fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap<Symbol, Ty<'tcx>>) {
- match pat.kind {
- PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
- PatKind::TupleStruct(_, pats, _) => {
- for pat in pats {
- bindings_impl(cx, pat, map);
- }
- },
- PatKind::Binding(.., ident, ref as_pat) => {
- if let Entry::Vacant(v) = map.entry(ident.name) {
- v.insert(cx.typeck_results().pat_ty(pat));
- }
- if let Some(ref as_pat) = *as_pat {
- bindings_impl(cx, as_pat, map);
- }
- },
- PatKind::Or(fields) | PatKind::Tuple(fields, _) => {
- for pat in fields {
- bindings_impl(cx, pat, map);
- }
- },
- PatKind::Struct(_, fields, _) => {
- for pat in fields {
- bindings_impl(cx, &pat.pat, map);
- }
- },
- PatKind::Slice(lhs, ref mid, rhs) => {
- for pat in lhs {
- bindings_impl(cx, pat, map);
- }
- if let Some(ref mid) = *mid {
- bindings_impl(cx, mid, map);
- }
- for pat in rhs {
- bindings_impl(cx, pat, map);
- }
- },
- PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (),
- }
- }
-
- let mut result = FxHashMap::default();
- bindings_impl(cx, pat, &mut result);
- result
-}
-
fn search_same_sequenced<T, Eq>(exprs: &[T], eq: Eq) -> Option<(&T, &T)>
where
Eq: Fn(&T, &T) -> bool,
@@ -370,47 +207,3 @@
}
None
}
-
-fn search_common_cases<'a, T, Eq>(exprs: &'a [T], eq: &Eq) -> Option<(&'a T, &'a T)>
-where
- Eq: Fn(&T, &T) -> bool,
-{
- if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) {
- Some((&exprs[0], &exprs[1]))
- } else {
- None
- }
-}
-
-fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
-where
- Hash: Fn(&T) -> u64,
- Eq: Fn(&T, &T) -> bool,
-{
- if let Some(expr) = search_common_cases(&exprs, &eq) {
- return vec![expr];
- }
-
- let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
-
- let mut map: FxHashMap<_, Vec<&_>> =
- FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
-
- for expr in exprs {
- match map.entry(hash(expr)) {
- Entry::Occupied(mut o) => {
- for o in o.get() {
- if eq(o, expr) {
- match_expr_list.push((o, expr));
- }
- }
- o.get_mut().push(expr);
- },
- Entry::Vacant(v) => {
- v.insert(vec![expr]);
- },
- }
- }
-
- match_expr_list
-}
diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs
new file mode 100644
index 0000000..612c535
--- /dev/null
+++ b/clippy_lints/src/default.rs
@@ -0,0 +1,304 @@
+use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet};
+use crate::utils::{span_lint_and_note, span_lint_and_sugg};
+use if_chain::if_chain;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::Applicability;
+use rustc_hir::def::Res;
+use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, Adt, Ty};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::{Ident, Symbol};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for literal calls to `Default::default()`.
+ ///
+ /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is
+ /// being gotten than the generic `Default`.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// // Bad
+ /// let s: String = Default::default();
+ ///
+ /// // Good
+ /// let s = String::default();
+ /// ```
+ pub DEFAULT_TRAIT_ACCESS,
+ pedantic,
+ "checks for literal calls to `Default::default()`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for immediate reassignment of fields initialized
+ /// with Default::default().
+ ///
+ /// **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).
+ ///
+ /// **Known problems:** Assignments to patterns that are of tuple type are not linted.
+ ///
+ /// **Example:**
+ /// Bad:
+ /// ```
+ /// # #[derive(Default)]
+ /// # struct A { i: i32 }
+ /// let mut a: A = Default::default();
+ /// a.i = 42;
+ /// ```
+ /// Use instead:
+ /// ```
+ /// # #[derive(Default)]
+ /// # struct A { i: i32 }
+ /// let a = A {
+ /// i: 42,
+ /// .. Default::default()
+ /// };
+ /// ```
+ pub FIELD_REASSIGN_WITH_DEFAULT,
+ style,
+ "binding initialized with Default should have its fields set in the initializer"
+}
+
+#[derive(Default)]
+pub struct Default {
+ // Spans linted by `field_reassign_with_default`.
+ reassigned_linted: FxHashSet<Span>,
+}
+
+impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
+
+impl LateLintPass<'_> for Default {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if_chain! {
+ // Avoid cases already linted by `field_reassign_with_default`
+ if !self.reassigned_linted.contains(&expr.span);
+ if let ExprKind::Call(ref path, ..) = expr.kind;
+ if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
+ if let ExprKind::Path(ref qpath) = path.kind;
+ if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
+ if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
+ // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
+ if let QPath::Resolved(None, _path) = qpath;
+ then {
+ let expr_ty = cx.typeck_results().expr_ty(expr);
+ if let ty::Adt(def, ..) = expr_ty.kind() {
+ // TODO: Work out a way to put "whatever the imported way of referencing
+ // this type in this file" rather than a fully-qualified type.
+ let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
+ span_lint_and_sugg(
+ cx,
+ DEFAULT_TRAIT_ACCESS,
+ expr.span,
+ &format!("calling `{}` is more clear than this expression", replacement),
+ "try",
+ replacement,
+ Applicability::Unspecified, // First resolve the TODO above
+ );
+ }
+ }
+ }
+ }
+
+ fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
+ // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
+ // `default` method of the `Default` trait, and store statement index in current block being
+ // checked and the name of the bound variable
+ let binding_statements_using_default = enumerate_bindings_using_default(cx, block);
+
+ // start from the `let mut _ = _::default();` and look at all the following
+ // statements, see if they re-assign the fields of the binding
+ for (stmt_idx, binding_name, binding_type, span) in binding_statements_using_default {
+ // the last statement of a block cannot trigger the lint
+ if stmt_idx == block.stmts.len() - 1 {
+ break;
+ }
+
+ // find all "later statement"'s where the fields of the binding set as
+ // Default::default() get reassigned, unless the reassignment refers to the original binding
+ let mut first_assign = None;
+ let mut assigned_fields = Vec::new();
+ let mut cancel_lint = false;
+ for consecutive_statement in &block.stmts[stmt_idx + 1..] {
+ // interrupt if the statement is a let binding (`Local`) that shadows the original
+ // binding
+ if stmt_shadows_binding(consecutive_statement, binding_name) {
+ break;
+ }
+ // find out if and which field was set by this `consecutive_statement`
+ else if let Some((field_ident, assign_rhs)) =
+ field_reassigned_by_stmt(consecutive_statement, binding_name)
+ {
+ // interrupt and cancel lint if assign_rhs references the original binding
+ if contains_name(binding_name, assign_rhs) {
+ cancel_lint = true;
+ break;
+ }
+
+ // if the field was previously assigned, replace the assignment, otherwise insert the assignment
+ if let Some(prev) = assigned_fields
+ .iter_mut()
+ .find(|(field_name, _)| field_name == &field_ident.name)
+ {
+ *prev = (field_ident.name, assign_rhs);
+ } else {
+ assigned_fields.push((field_ident.name, assign_rhs));
+ }
+
+ // also set first instance of error for help message
+ if first_assign.is_none() {
+ first_assign = Some(consecutive_statement);
+ }
+ }
+ // interrupt also if no field was assigned, since we only want to look at consecutive statements
+ else {
+ break;
+ }
+ }
+
+ // if there are incorrectly assigned fields, do a span_lint_and_note to suggest
+ // construction using `Ty { fields, ..Default::default() }`
+ if !assigned_fields.is_empty() && !cancel_lint {
+ // take the original assignment as span
+ let stmt = &block.stmts[stmt_idx];
+
+ if let StmtKind::Local(preceding_local) = &stmt.kind {
+ // filter out fields like `= Default::default()`, because the FRU already covers them
+ let assigned_fields = assigned_fields
+ .into_iter()
+ .filter(|(_, rhs)| !is_expr_default(rhs, cx))
+ .collect::<Vec<(Symbol, &Expr<'_>)>>();
+
+ // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
+ let ext_with_default = !fields_of_type(binding_type)
+ .iter()
+ .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name));
+
+ let field_list = assigned_fields
+ .into_iter()
+ .map(|(field, rhs)| {
+ // extract and store the assigned value for help message
+ let value_snippet = snippet(cx, rhs.span, "..");
+ format!("{}: {}", field, value_snippet)
+ })
+ .collect::<Vec<String>>()
+ .join(", ");
+
+ let sugg = if ext_with_default {
+ if field_list.is_empty() {
+ format!("{}::default()", binding_type)
+ } else {
+ format!("{} {{ {}, ..Default::default() }}", binding_type, field_list)
+ }
+ } else {
+ format!("{} {{ {} }}", binding_type, field_list)
+ };
+
+ // span lint once per statement that binds default
+ span_lint_and_note(
+ cx,
+ FIELD_REASSIGN_WITH_DEFAULT,
+ first_assign.unwrap().span,
+ "field assignment outside of initializer for an instance created with Default::default()",
+ Some(preceding_local.span),
+ &format!(
+ "consider initializing the variable with `{}` and removing relevant reassignments",
+ sugg
+ ),
+ );
+ self.reassigned_linted.insert(span);
+ }
+ }
+ }
+ }
+}
+
+/// Checks if the given expression is the `default` method belonging to the `Default` trait.
+fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool {
+ if_chain! {
+ if let ExprKind::Call(ref fn_expr, _) = &expr.kind;
+ if let ExprKind::Path(qpath) = &fn_expr.kind;
+ if let Res::Def(_, def_id) = qpath_res(cx, qpath, fn_expr.hir_id);
+ then {
+ // right hand side of assignment is `Default::default`
+ match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD)
+ } else {
+ false
+ }
+ }
+}
+
+/// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except
+/// for when the pattern type is a tuple.
+fn enumerate_bindings_using_default<'tcx>(
+ cx: &LateContext<'tcx>,
+ block: &Block<'tcx>,
+) -> Vec<(usize, Symbol, Ty<'tcx>, Span)> {
+ block
+ .stmts
+ .iter()
+ .enumerate()
+ .filter_map(|(idx, stmt)| {
+ if_chain! {
+ // only take `let ...` statements
+ if let StmtKind::Local(ref local) = stmt.kind;
+ // only take bindings to identifiers
+ if let PatKind::Binding(_, _, ident, _) = local.pat.kind;
+ // that are not tuples
+ let ty = cx.typeck_results().pat_ty(local.pat);
+ if !matches!(ty.kind(), ty::Tuple(_));
+ // only when assigning `... = Default::default()`
+ if let Some(ref expr) = local.init;
+ if is_expr_default(expr, cx);
+ then {
+ Some((idx, ident.name, ty, expr.span))
+ } else {
+ None
+ }
+ }
+ })
+ .collect()
+}
+
+fn stmt_shadows_binding(this: &Stmt<'_>, shadowed: Symbol) -> bool {
+ if let StmtKind::Local(local) = &this.kind {
+ if let PatKind::Binding(_, _, ident, _) = local.pat.kind {
+ return ident.name == shadowed;
+ }
+ }
+ false
+}
+
+/// Returns the reassigned field and the assigning expression (right-hand side of assign).
+fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
+ if_chain! {
+ // only take assignments
+ if let StmtKind::Semi(ref later_expr) = this.kind;
+ if let ExprKind::Assign(ref assign_lhs, ref assign_rhs, _) = later_expr.kind;
+ // only take assignments to fields where the left-hand side field is a field of
+ // the same binding as the previous statement
+ if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind;
+ if let ExprKind::Path(ref qpath) = binding.kind;
+ if let QPath::Resolved(_, path) = qpath;
+ if let Some(second_binding_name) = path.segments.last();
+ if second_binding_name.ident.name == binding_name;
+ then {
+ Some((field_ident, assign_rhs))
+ } else {
+ None
+ }
+ }
+}
+
+/// Returns the vec of fields for a struct and an empty vec for non-struct ADTs.
+fn fields_of_type(ty: Ty<'_>) -> Vec<Ident> {
+ if let Adt(adt, _) = ty.kind() {
+ if adt.is_struct() {
+ let variant = &adt.non_enum_variant();
+ return variant.fields.iter().map(|f| f.ident).collect();
+ }
+ }
+ vec![]
+}
diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs
deleted file mode 100644
index 3048436..0000000
--- a/clippy_lints/src/default_trait_access.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, QPath};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-use crate::utils::{any_parent_is_automatically_derived, match_def_path, paths, span_lint_and_sugg};
-
-declare_clippy_lint! {
- /// **What it does:** Checks for literal calls to `Default::default()`.
- ///
- /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is
- /// being gotten than the generic `Default`.
- ///
- /// **Known problems:** None.
- ///
- /// **Example:**
- /// ```rust
- /// // Bad
- /// let s: String = Default::default();
- ///
- /// // Good
- /// let s = String::default();
- /// ```
- pub DEFAULT_TRAIT_ACCESS,
- pedantic,
- "checks for literal calls to `Default::default()`"
-}
-
-declare_lint_pass!(DefaultTraitAccess => [DEFAULT_TRAIT_ACCESS]);
-
-impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if_chain! {
- if let ExprKind::Call(ref path, ..) = expr.kind;
- if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
- if let ExprKind::Path(ref qpath) = path.kind;
- if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
- if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
- // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
- if let QPath::Resolved(None, _path) = qpath;
- then {
- let expr_ty = cx.typeck_results().expr_ty(expr);
- if let ty::Adt(def, ..) = expr_ty.kind() {
- // TODO: Work out a way to put "whatever the imported way of referencing
- // this type in this file" rather than a fully-qualified type.
- let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
- span_lint_and_sugg(
- cx,
- DEFAULT_TRAIT_ACCESS,
- expr.span,
- &format!("calling `{}` is more clear than this expression", replacement),
- "try",
- replacement,
- Applicability::Unspecified, // First resolve the TODO above
- );
- }
- }
- }
- }
-}
diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs
index c588436..461c6e3 100644
--- a/clippy_lints/src/deprecated_lints.rs
+++ b/clippy_lints/src/deprecated_lints.rs
@@ -172,3 +172,12 @@
pub DROP_BOUNDS,
"this lint has been uplifted to rustc and is now called `drop_bounds`"
}
+
+declare_deprecated_lint! {
+ /// **What it does:** Nothing. This lint has been deprecated.
+ ///
+ /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
+ /// `temporary_cstring_as_ptr`.
+ pub TEMPORARY_CSTRING_AS_PTR,
+ "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`"
+}
diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs
index bf8e030..c75efc6 100644
--- a/clippy_lints/src/derive.rs
+++ b/clippy_lints/src/derive.rs
@@ -1,6 +1,6 @@
use crate::utils::paths;
use crate::utils::{
- get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_path, span_lint_and_help,
+ get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_def_path, match_path, span_lint_and_help,
span_lint_and_note, span_lint_and_then,
};
use if_chain::if_chain;
@@ -193,10 +193,9 @@
hash_is_automatically_derived: bool,
) {
if_chain! {
- if match_path(&trait_ref.path, &paths::HASH);
if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
- if let Some(def_id) = &trait_ref.trait_def_id();
- if !def_id.is_local();
+ if let Some(def_id) = trait_ref.trait_def_id();
+ if match_def_path(cx, def_id, &paths::HASH);
then {
// Look for the PartialEq implementations for `ty`
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
@@ -352,7 +351,8 @@
}
if_chain! {
- if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE);
+ if let Some(trait_def_id) = trait_ref.trait_def_id();
+ if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE);
if let ty::Adt(def, _) = ty.kind();
if let Some(local_def_id) = def.did.as_local();
let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs
index 07f604c..edecba5 100644
--- a/clippy_lints/src/doc.rs
+++ b/clippy_lints/src/doc.rs
@@ -15,7 +15,7 @@
use rustc_session::parse::ParseSess;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span};
-use rustc_span::{FileName, Pos};
+use rustc_span::{sym, FileName, Pos};
use std::io;
use std::ops::Range;
use url::Url;
@@ -237,7 +237,7 @@
);
}
if !headers.errors {
- if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) {
+ if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) {
span_lint(
cx,
MISSING_ERRORS_DOC,
@@ -255,7 +255,7 @@
if let ty::Opaque(_, subs) = ret_ty.kind();
if let Some(gen) = subs.types().next();
if let ty::Generator(_, subs, _) = gen.kind();
- if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym!(result_type));
+ if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::result_type);
then {
span_lint(
cx,
@@ -333,7 +333,7 @@
let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span);
spans.extend_from_slice(¤t_spans);
doc.push_str(&comment);
- } else if attr.has_name(sym!(doc)) {
+ } else if attr.has_name(sym::doc) {
// ignore mix of sugared and non-sugared doc
// don't trigger the safety or errors check
return DocHeaders {
@@ -479,7 +479,7 @@
| ItemKind::ExternCrate(..)
| ItemKind::ForeignMod(..) => return false,
// We found a main function ...
- ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym!(main) => {
+ ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => {
let is_async = matches!(sig.header.asyncness, Async::Yes{..});
let returns_nothing = match &sig.decl.output {
FnRetTy::Default(..) => true,
diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs
index 8b02291..1bf3b81 100644
--- a/clippy_lints/src/escape.rs
+++ b/clippy_lints/src/escape.rs
@@ -29,7 +29,6 @@
/// **Example:**
/// ```rust
/// # fn foo(bar: usize) {}
- ///
/// // Bad
/// let x = Box::new(1);
/// foo(*x);
diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs
index 0240e80..f8038d0 100644
--- a/clippy_lints/src/explicit_write.rs
+++ b/clippy_lints/src/explicit_write.rs
@@ -5,6 +5,7 @@
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be
@@ -33,7 +34,7 @@
if_chain! {
// match call to unwrap
if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args, _) = expr.kind;
- if unwrap_fun.ident.name == sym!(unwrap);
+ if unwrap_fun.ident.name == sym::unwrap;
// match call to write_fmt
if !unwrap_args.is_empty();
if let ExprKind::MethodCall(ref write_fun, _, write_args, _) =
diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs
index a9e05fd..fe817fe 100644
--- a/clippy_lints/src/fallible_impl_from.rs
+++ b/clippy_lints/src/fallible_impl_from.rs
@@ -6,7 +6,7 @@
use rustc_middle::hir::map::Map;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::Span;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
@@ -95,8 +95,8 @@
// check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
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))
+ if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type)
+ || is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type)
{
self.result.push(expr.span);
}
@@ -113,7 +113,7 @@
for impl_item in impl_items {
if_chain! {
- if impl_item.ident.name == sym!(from);
+ if impl_item.ident.name == sym::from;
if let ImplItemKind::Fn(_, body_id) =
cx.tcx.hir().impl_item(impl_item.id).kind;
then {
diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs
index 69818b4..c1c0859 100644
--- a/clippy_lints/src/float_equality_without_abs.rs
+++ b/clippy_lints/src/float_equality_without_abs.rs
@@ -1,7 +1,8 @@
-use crate::utils::{match_qpath, paths, span_lint_and_then, sugg};
+use crate::utils::{match_def_path, paths, span_lint_and_then, sugg};
use if_chain::if_chain;
use rustc_ast::util::parser::AssocOp;
use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
@@ -76,7 +77,8 @@
// right hand side matches either f32::EPSILON or f64::EPSILON
if let ExprKind::Path(ref epsilon_path) = rhs.kind;
- if match_qpath(epsilon_path, &paths::F32_EPSILON) || match_qpath(epsilon_path, &paths::F64_EPSILON);
+ if let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id);
+ if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON);
// values of the substractions on the left hand side are of the type float
let t_val_l = cx.typeck_results().expr_ty(val_l);
diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs
index 26da058..8e41e0e 100644
--- a/clippy_lints/src/format.rs
+++ b/clippy_lints/src/format.rs
@@ -10,6 +10,7 @@
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for the use of `format!("string literal with no
@@ -91,7 +92,7 @@
if pats.len() == 1;
then {
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)) {
+ if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym::string_type) {
return None;
}
if let ExprKind::Lit(ref lit) = format_args.kind {
@@ -186,15 +187,15 @@
if exprs.len() == 1;
// struct `core::fmt::rt::v1::Argument`
if let ExprKind::Struct(_, ref fields, _) = exprs[0].kind;
- if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym!(format));
+ if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
// struct `core::fmt::rt::v1::FormatSpec`
if let ExprKind::Struct(_, ref fields, _) = format_field.expr.kind;
- if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym!(precision));
+ if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym::precision);
if let ExprKind::Path(ref precision_path) = precision_field.expr.kind;
- if last_path_segment(precision_path).ident.name == sym!(Implied);
- if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym!(width));
+ if last_path_segment(precision_path).ident.name == sym::Implied;
+ if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym::width);
if let ExprKind::Path(ref width_qpath) = width_field.expr.kind;
- if last_path_segment(width_qpath).ident.name == sym!(Implied);
+ if last_path_segment(width_qpath).ident.name == sym::Implied;
then {
return true;
}
diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs
index 9c0efef..8b58d1f 100644
--- a/clippy_lints/src/functions.rs
+++ b/clippy_lints/src/functions.rs
@@ -16,6 +16,7 @@
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
+use rustc_span::sym;
use rustc_target::spec::abi::Abi;
use rustc_typeck::hir_ty_to_ty;
@@ -473,7 +474,7 @@
if !in_external_macro(cx.sess(), item_span);
if let hir::FnRetTy::Return(ref ty) = decl.output;
if let hir::TyKind::Path(ref qpath) = ty.kind;
- if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym!(result_type));
+ if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym::result_type);
if let Some(ref args) = last_path_segment(qpath).args;
if let [_, hir::GenericArg::Type(ref err_ty)] = args.args;
if let hir::TyKind::Tup(t) = err_ty.kind;
diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs
index 48ebcf5..cdd8a42 100644
--- a/clippy_lints/src/get_last_with_len.rs
+++ b/clippy_lints/src/get_last_with_len.rs
@@ -8,6 +8,7 @@
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for using `x.get(x.len() - 1)` instead of
@@ -55,7 +56,7 @@
// Argument 0 (the struct we're calling the method on) is a vector
if let Some(struct_calling_on) = args.get(0);
let struct_ty = cx.typeck_results().expr_ty(struct_calling_on);
- if is_type_diagnostic_item(cx, struct_ty, sym!(vec_type));
+ if is_type_diagnostic_item(cx, struct_ty, sym::vec_type);
// Argument to "get" is a subtraction
if let Some(get_index_arg) = args.get(1);
diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs
index 28b20cd..e0a1f4c 100644
--- a/clippy_lints/src/if_let_some_result.rs
+++ b/clippy_lints/src/if_let_some_result.rs
@@ -4,6 +4,7 @@
use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:*** Checks for unnecessary `ok()` in if let.
@@ -45,7 +46,7 @@
if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = op.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation
if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
- if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym!(result_type));
+ if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type);
if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
then {
diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs
index 0877b44..b723d06 100644
--- a/clippy_lints/src/inherent_to_string.rs
+++ b/clippy_lints/src/inherent_to_string.rs
@@ -2,6 +2,7 @@
use rustc_hir::{ImplItem, ImplItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
use crate::utils::{
get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help,
@@ -107,7 +108,7 @@
if decl.inputs.len() == 1;
// Check if return type is String
- if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type));
+ if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::string_type);
// Filters instances of to_string which are required by a trait
if trait_ref_of_method(cx, impl_item.hir_id).is_none();
diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs
index 4b605fd..d1c3fdc 100644
--- a/clippy_lints/src/inline_fn_without_body.rs
+++ b/clippy_lints/src/inline_fn_without_body.rs
@@ -7,7 +7,7 @@
use rustc_hir::{TraitFn, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::Symbol;
+use rustc_span::{sym, Symbol};
declare_clippy_lint! {
/// **What it does:** Checks for `#[inline]` on trait methods without bodies
@@ -41,7 +41,7 @@
fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) {
for attr in attrs {
- if !attr.has_name(sym!(inline)) {
+ if !attr.has_name(sym::inline) {
continue;
}
diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs
index c8576bc..8998fae 100644
--- a/clippy_lints/src/items_after_statements.rs
+++ b/clippy_lints/src/items_after_statements.rs
@@ -2,7 +2,8 @@
use crate::utils::span_lint;
use rustc_ast::ast::{Block, ItemKind, StmtKind};
-use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
@@ -53,7 +54,7 @@
impl EarlyLintPass for ItemsAfterStatements {
fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) {
- if item.span.from_expansion() {
+ if in_external_macro(cx.sess(), item.span) {
return;
}
@@ -67,7 +68,7 @@
// lint on all further items
for stmt in stmts {
if let StmtKind::Item(ref it) = *stmt {
- if it.span.from_expansion() {
+ if in_external_macro(cx.sess(), it.span) {
return;
}
if let ItemKind::MacroDef(..) = it.kind {
diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs
index c9c4891..8e2f03d 100644
--- a/clippy_lints/src/len_zero.rs
+++ b/clippy_lints/src/len_zero.rs
@@ -68,7 +68,44 @@
"traits or impls with a public `len` method but no corresponding `is_empty` method"
}
-declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY]);
+declare_clippy_lint! {
+ /// **What it does:** Checks for comparing to an empty slice such as "" or [],`
+ /// and suggests using `.is_empty()` where applicable.
+ ///
+ /// **Why is this bad?** Some structures can answer `.is_empty()` much faster
+ /// than checking for equality. So it is good to get into the habit of using
+ /// `.is_empty()`, and having it is cheap.
+ /// Besides, it makes the intent clearer than a manual comparison in some contexts.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```ignore
+ /// if s == "" {
+ /// ..
+ /// }
+ ///
+ /// if arr == [] {
+ /// ..
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```ignore
+ /// if s.is_empty() {
+ /// ..
+ /// }
+ ///
+ /// if arr.is_empty() {
+ /// ..
+ /// }
+ /// ```
+ pub COMPARISON_TO_EMPTY,
+ style,
+ "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
+}
+
+declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]);
impl<'tcx> LateLintPass<'tcx> for LenZero {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -221,6 +258,8 @@
}
check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to)
+ } else {
+ check_empty_expr(cx, span, method, lit, op)
}
}
@@ -258,6 +297,42 @@
}
}
+fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) {
+ if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ COMPARISON_TO_EMPTY,
+ span,
+ "comparison to empty slice",
+ &format!("using `{}is_empty` is clearer and more explicit", op),
+ format!(
+ "{}{}.is_empty()",
+ op,
+ snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
+ ),
+ applicability,
+ );
+ }
+}
+
+fn is_empty_string(expr: &Expr<'_>) -> bool {
+ if let ExprKind::Lit(ref lit) = expr.kind {
+ if let LitKind::Str(lit, _) = lit.node {
+ let lit = lit.as_str();
+ return lit == "";
+ }
+ }
+ false
+}
+
+fn is_empty_array(expr: &Expr<'_>) -> bool {
+ if let ExprKind::Array(ref arr) = expr.kind {
+ return arr.is_empty();
+ }
+ false
+}
+
/// Checks if this type has an `is_empty` method.
fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
/// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index b330b66..126852d 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -11,6 +11,7 @@
#![feature(or_patterns)]
#![feature(rustc_private)]
#![feature(stmt_expr_attributes)]
+#![feature(control_flow_enum)]
#![recursion_limit = "512"]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
@@ -175,7 +176,7 @@
mod copy_iterator;
mod create_dir;
mod dbg_macro;
-mod default_trait_access;
+mod default;
mod dereference;
mod derive;
mod disallowed_method;
@@ -233,6 +234,7 @@
mod main_recursion;
mod manual_async_fn;
mod manual_non_exhaustive;
+mod manual_ok_or;
mod manual_strip;
mod manual_unwrap_or;
mod map_clone;
@@ -293,6 +295,7 @@
mod redundant_field_names;
mod redundant_pub_crate;
mod redundant_static_lifetimes;
+mod ref_option_ref;
mod reference;
mod regex;
mod repeat_once;
@@ -488,6 +491,10 @@
"clippy::drop_bounds",
"this lint has been uplifted to rustc and is now called `drop_bounds`",
);
+ store.register_removed(
+ "clippy::temporary_cstring_as_ptr",
+ "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`",
+ );
// end deprecated lints, do not remove this comment, it’s used in `update_lints`
// begin register lints, do not remove this comment, it’s used in `update_lints`
@@ -528,12 +535,12 @@
&comparison_chain::COMPARISON_CHAIN,
&copies::IFS_SAME_COND,
&copies::IF_SAME_THEN_ELSE,
- &copies::MATCH_SAME_ARMS,
&copies::SAME_FUNCTIONS_IN_IF_CONDITION,
©_iterator::COPY_ITERATOR,
&create_dir::CREATE_DIR,
&dbg_macro::DBG_MACRO,
- &default_trait_access::DEFAULT_TRAIT_ACCESS,
+ &default::DEFAULT_TRAIT_ACCESS,
+ &default::FIELD_REASSIGN_WITH_DEFAULT,
&dereference::EXPLICIT_DEREF_METHODS,
&derive::DERIVE_HASH_XOR_EQ,
&derive::DERIVE_ORD_XOR_PARTIAL_ORD,
@@ -611,6 +618,7 @@
&large_const_arrays::LARGE_CONST_ARRAYS,
&large_enum_variant::LARGE_ENUM_VARIANT,
&large_stack_arrays::LARGE_STACK_ARRAYS,
+ &len_zero::COMPARISON_TO_EMPTY,
&len_zero::LEN_WITHOUT_IS_EMPTY,
&len_zero::LEN_ZERO,
&let_if_seq::USELESS_LET_IF_SEQ,
@@ -623,6 +631,7 @@
&literal_representation::LARGE_DIGIT_GROUPS,
&literal_representation::MISTYPED_LITERAL_SUFFIXES,
&literal_representation::UNREADABLE_LITERAL,
+ &literal_representation::UNUSUAL_BYTE_GROUPINGS,
&loops::EMPTY_LOOP,
&loops::EXPLICIT_COUNTER_LOOP,
&loops::EXPLICIT_INTO_ITER_LOOP,
@@ -644,6 +653,7 @@
&main_recursion::MAIN_RECURSION,
&manual_async_fn::MANUAL_ASYNC_FN,
&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
+ &manual_ok_or::MANUAL_OK_OR,
&manual_strip::MANUAL_STRIP,
&manual_unwrap_or::MANUAL_UNWRAP_OR,
&map_clone::MAP_CLONE,
@@ -658,6 +668,7 @@
&matches::MATCH_LIKE_MATCHES_MACRO,
&matches::MATCH_OVERLAPPING_ARM,
&matches::MATCH_REF_PATS,
+ &matches::MATCH_SAME_ARMS,
&matches::MATCH_SINGLE_BINDING,
&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
&matches::MATCH_WILD_ERR_ARM,
@@ -686,6 +697,7 @@
&methods::FILTER_NEXT,
&methods::FIND_MAP,
&methods::FLAT_MAP_IDENTITY,
+ &methods::FROM_ITER_INSTEAD_OF_COLLECT,
&methods::GET_UNWRAP,
&methods::INEFFICIENT_TO_STRING,
&methods::INTO_ITER_ON_REF,
@@ -696,6 +708,7 @@
&methods::ITER_NTH_ZERO,
&methods::ITER_SKIP_NEXT,
&methods::MANUAL_SATURATING_ARITHMETIC,
+ &methods::MAP_COLLECT_RESULT_UNIT,
&methods::MAP_FLATTEN,
&methods::MAP_UNWRAP_OR,
&methods::NEW_RET_NO_SELF,
@@ -706,12 +719,11 @@
&methods::RESULT_MAP_OR_INTO_OPTION,
&methods::SEARCH_IS_SOME,
&methods::SHOULD_IMPLEMENT_TRAIT,
+ &methods::SINGLE_CHAR_ADD_STR,
&methods::SINGLE_CHAR_PATTERN,
- &methods::SINGLE_CHAR_PUSH_STR,
&methods::SKIP_WHILE_NEXT,
&methods::STRING_EXTEND_CHARS,
&methods::SUSPICIOUS_MAP,
- &methods::TEMPORARY_CSTRING_AS_PTR,
&methods::UNINIT_ASSUMED_INIT,
&methods::UNNECESSARY_FILTER_MAP,
&methods::UNNECESSARY_FOLD,
@@ -802,6 +814,7 @@
&redundant_field_names::REDUNDANT_FIELD_NAMES,
&redundant_pub_crate::REDUNDANT_PUB_CRATE,
&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
+ &ref_option_ref::REF_OPTION_REF,
&reference::DEREF_ADDROF,
&reference::REF_IN_DEREF,
®ex::INVALID_REGEX,
@@ -891,6 +904,7 @@
&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
&utils::internal_lints::COMPILER_LINT_FUNCTIONS,
&utils::internal_lints::DEFAULT_LINT,
+ &utils::internal_lints::INVALID_PATHS,
&utils::internal_lints::LINT_WITHOUT_LINT_PASS,
&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
&utils::internal_lints::OUTER_EXPN_EXPN_DATA,
@@ -918,6 +932,7 @@
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
+ store.register_late_pass(|| box utils::internal_lints::InvalidPaths);
store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
store.register_late_pass(|| box utils::author::Author);
let vec_box_size_threshold = conf.vec_box_size_threshold;
@@ -1023,6 +1038,7 @@
&sess.target,
);
store.register_late_pass(move || box pass_by_ref_or_value);
+ store.register_late_pass(|| box ref_option_ref::RefOptionRef);
store.register_late_pass(|| box try_err::TryErr);
store.register_late_pass(|| box use_self::UseSelf);
store.register_late_pass(|| box bytecount::ByteCount);
@@ -1040,7 +1056,6 @@
store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd);
store.register_late_pass(|| box unwrap::Unwrap);
store.register_late_pass(|| box duration_subsec::DurationSubsec);
- store.register_late_pass(|| box default_trait_access::DefaultTraitAccess);
store.register_late_pass(|| box indexing_slicing::IndexingSlicing);
store.register_late_pass(|| box non_copy_const::NonCopyConst);
store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast);
@@ -1091,6 +1106,7 @@
let enum_variant_name_threshold = conf.enum_variant_name_threshold;
store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
+ store.register_late_pass(|| box default::Default::default());
store.register_late_pass(|| box unused_self::UnusedSelf);
store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
store.register_late_pass(|| box exit::Exit);
@@ -1139,6 +1155,7 @@
store.register_late_pass(|| box unwrap_in_result::UnwrapInResult);
store.register_late_pass(|| box self_assignment::SelfAssignment);
store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr);
+ store.register_late_pass(|| box manual_ok_or::ManualOkOr);
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);
@@ -1201,10 +1218,9 @@
LintId::of(&attrs::INLINE_ALWAYS),
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),
LintId::of(©_iterator::COPY_ITERATOR),
- LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS),
+ LintId::of(&default::DEFAULT_TRAIT_ACCESS),
LintId::of(&dereference::EXPLICIT_DEREF_METHODS),
LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY),
LintId::of(&derive::UNSAFE_DERIVE_DESERIALIZE),
@@ -1228,9 +1244,11 @@
LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP),
LintId::of(&loops::EXPLICIT_ITER_LOOP),
LintId::of(¯o_use::MACRO_USE_IMPORTS),
+ LintId::of(&manual_ok_or::MANUAL_OK_OR),
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_SAME_ARMS),
LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
LintId::of(&matches::MATCH_WILD_ERR_ARM),
LintId::of(&matches::SINGLE_MATCH_ELSE),
@@ -1251,6 +1269,7 @@
LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
LintId::of(&ranges::RANGE_MINUS_ONE),
LintId::of(&ranges::RANGE_PLUS_ONE),
+ LintId::of(&ref_option_ref::REF_OPTION_REF),
LintId::of(&shadow::SHADOW_UNRELATED),
LintId::of(&strings::STRING_ADD_ASSIGN),
LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
@@ -1279,6 +1298,7 @@
LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
LintId::of(&utils::internal_lints::DEFAULT_LINT),
+ LintId::of(&utils::internal_lints::INVALID_PATHS),
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),
@@ -1311,6 +1331,7 @@
LintId::of(&comparison_chain::COMPARISON_CHAIN),
LintId::of(&copies::IFS_SAME_COND),
LintId::of(&copies::IF_SAME_THEN_ELSE),
+ LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT),
LintId::of(&derive::DERIVE_HASH_XOR_EQ),
LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD),
LintId::of(&doc::MISSING_SAFETY_DOC),
@@ -1358,6 +1379,7 @@
LintId::of(&int_plus_one::INT_PLUS_ONE),
LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS),
LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
+ LintId::of(&len_zero::COMPARISON_TO_EMPTY),
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
LintId::of(&len_zero::LEN_ZERO),
LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
@@ -1365,6 +1387,7 @@
LintId::of(&lifetimes::NEEDLESS_LIFETIMES),
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES),
+ LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS),
LintId::of(&loops::EMPTY_LOOP),
LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
LintId::of(&loops::FOR_KV_MAP),
@@ -1410,6 +1433,7 @@
LintId::of(&methods::EXPECT_FUN_CALL),
LintId::of(&methods::FILTER_NEXT),
LintId::of(&methods::FLAT_MAP_IDENTITY),
+ LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(&methods::INTO_ITER_ON_REF),
LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
LintId::of(&methods::ITER_CLONED_COLLECT),
@@ -1418,6 +1442,7 @@
LintId::of(&methods::ITER_NTH_ZERO),
LintId::of(&methods::ITER_SKIP_NEXT),
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
+ LintId::of(&methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(&methods::NEW_RET_NO_SELF),
LintId::of(&methods::OK_EXPECT),
LintId::of(&methods::OPTION_AS_REF_DEREF),
@@ -1426,12 +1451,11 @@
LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION),
LintId::of(&methods::SEARCH_IS_SOME),
LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT),
+ LintId::of(&methods::SINGLE_CHAR_ADD_STR),
LintId::of(&methods::SINGLE_CHAR_PATTERN),
- LintId::of(&methods::SINGLE_CHAR_PUSH_STR),
LintId::of(&methods::SKIP_WHILE_NEXT),
LintId::of(&methods::STRING_EXTEND_CHARS),
LintId::of(&methods::SUSPICIOUS_MAP),
- LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR),
LintId::of(&methods::UNINIT_ASSUMED_INIT),
LintId::of(&methods::UNNECESSARY_FILTER_MAP),
LintId::of(&methods::UNNECESSARY_FOLD),
@@ -1569,6 +1593,7 @@
LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
LintId::of(&collapsible_if::COLLAPSIBLE_IF),
LintId::of(&comparison_chain::COMPARISON_CHAIN),
+ LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT),
LintId::of(&doc::MISSING_SAFETY_DOC),
LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),
LintId::of(&enum_variants::ENUM_VARIANT_NAMES),
@@ -1584,9 +1609,11 @@
LintId::of(&functions::RESULT_UNIT_ERR),
LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
LintId::of(&inherent_to_string::INHERENT_TO_STRING),
+ LintId::of(&len_zero::COMPARISON_TO_EMPTY),
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
LintId::of(&len_zero::LEN_ZERO),
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
+ LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS),
LintId::of(&loops::EMPTY_LOOP),
LintId::of(&loops::FOR_KV_MAP),
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
@@ -1606,18 +1633,20 @@
LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
LintId::of(&methods::CHARS_LAST_CMP),
LintId::of(&methods::CHARS_NEXT_CMP),
+ LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(&methods::INTO_ITER_ON_REF),
LintId::of(&methods::ITER_CLONED_COLLECT),
LintId::of(&methods::ITER_NEXT_SLICE),
LintId::of(&methods::ITER_NTH_ZERO),
LintId::of(&methods::ITER_SKIP_NEXT),
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
+ LintId::of(&methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(&methods::NEW_RET_NO_SELF),
LintId::of(&methods::OK_EXPECT),
LintId::of(&methods::OPTION_MAP_OR_NONE),
LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION),
LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT),
- LintId::of(&methods::SINGLE_CHAR_PUSH_STR),
+ LintId::of(&methods::SINGLE_CHAR_ADD_STR),
LintId::of(&methods::STRING_EXTEND_CHARS),
LintId::of(&methods::UNNECESSARY_FOLD),
LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS),
@@ -1787,7 +1816,6 @@
LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT),
LintId::of(&methods::CLONE_DOUBLE_REF),
LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
- LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR),
LintId::of(&methods::UNINIT_ASSUMED_INIT),
LintId::of(&methods::ZST_OFFSET),
LintId::of(&minmax::MIN_MAX),
@@ -1945,6 +1973,7 @@
ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles");
ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
+ ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
}
// only exists to let the dogfood integration test works.
diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs
index c8a5a9c..4d737b3 100644
--- a/clippy_lints/src/lifetimes.rs
+++ b/clippy_lints/src/lifetimes.rs
@@ -16,7 +16,6 @@
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, Symbol};
-use std::iter::FromIterator;
declare_clippy_lint! {
/// **What it does:** Checks for lifetime annotations which can be removed by
@@ -214,14 +213,15 @@
}
if allowed_lts
- .intersection(&FxHashSet::from_iter(
- input_visitor
+ .intersection(
+ &input_visitor
.nested_elision_site_lts
.iter()
.chain(output_visitor.nested_elision_site_lts.iter())
.cloned()
- .filter(|v| matches!(v, RefLt::Named(_))),
- ))
+ .filter(|v| matches!(v, RefLt::Named(_)))
+ .collect(),
+ )
.next()
.is_some()
{
diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs
index c54103b..e8a7416 100644
--- a/clippy_lints/src/literal_representation.rs
+++ b/clippy_lints/src/literal_representation.rs
@@ -83,6 +83,25 @@
}
declare_clippy_lint! {
+ /// **What it does:** Warns if hexadecimal or binary literals are not grouped
+ /// by nibble or byte.
+ ///
+ /// **Why is this bad?** Negatively impacts readability.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// let x: u32 = 0xFFF_FFF;
+ /// let y: u8 = 0b01_011_101;
+ /// ```
+ pub UNUSUAL_BYTE_GROUPINGS,
+ style,
+ "binary or hex literals that aren't grouped by four"
+}
+
+declare_clippy_lint! {
/// **What it does:** Warns if the digits of an integral or floating-point
/// constant are grouped into groups that
/// are too large.
@@ -125,6 +144,7 @@
LargeDigitGroups,
DecimalRepresentation,
MistypedLiteralSuffix,
+ UnusualByteGroupings,
}
impl WarningType {
@@ -175,6 +195,15 @@
suggested_format,
Applicability::MachineApplicable,
),
+ Self::UnusualByteGroupings => span_lint_and_sugg(
+ cx,
+ UNUSUAL_BYTE_GROUPINGS,
+ span,
+ "digits of hex or binary literal not grouped by four",
+ "consider",
+ suggested_format,
+ Applicability::MachineApplicable,
+ ),
};
}
}
@@ -184,6 +213,7 @@
INCONSISTENT_DIGIT_GROUPING,
LARGE_DIGIT_GROUPS,
MISTYPED_LITERAL_SUFFIXES,
+ UNUSUAL_BYTE_GROUPINGS,
]);
impl EarlyLintPass for LiteralDigitGrouping {
@@ -217,9 +247,9 @@
let result = (|| {
- let integral_group_size = Self::get_group_size(num_lit.integer.split('_'))?;
+ let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix)?;
if let Some(fraction) = num_lit.fraction {
- let fractional_group_size = Self::get_group_size(fraction.rsplit('_'))?;
+ let fractional_group_size = Self::get_group_size(fraction.rsplit('_'), num_lit.radix)?;
let consistent = Self::parts_consistent(integral_group_size,
fractional_group_size,
@@ -229,6 +259,7 @@
return Err(WarningType::InconsistentDigitGrouping);
};
}
+
Ok(())
})();
@@ -237,6 +268,7 @@
let should_warn = match warning_type {
| WarningType::UnreadableLiteral
| WarningType::InconsistentDigitGrouping
+ | WarningType::UnusualByteGroupings
| WarningType::LargeDigitGroups => {
!in_macro(lit.span)
}
@@ -331,11 +363,15 @@
/// Returns the size of the digit groups (or None if ungrouped) if successful,
/// otherwise returns a `WarningType` for linting.
- fn get_group_size<'a>(groups: impl Iterator<Item = &'a str>) -> Result<Option<usize>, WarningType> {
+ fn get_group_size<'a>(groups: impl Iterator<Item = &'a str>, radix: Radix) -> Result<Option<usize>, WarningType> {
let mut groups = groups.map(str::len);
let first = groups.next().expect("At least one group");
+ if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 && i != 2) {
+ return Err(WarningType::UnusualByteGroupings);
+ }
+
if let Some(second) = groups.next() {
if !groups.all(|x| x == second) || first > second {
Err(WarningType::InconsistentDigitGrouping)
diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs
index 23ca35f..c7d3f4e 100644
--- a/clippy_lints/src/loops.rs
+++ b/clippy_lints/src/loops.rs
@@ -619,9 +619,9 @@
}
let lhs_constructor = last_path_segment(qpath);
- if method_path.ident.name == sym!(next)
+ if method_path.ident.name == sym::next
&& match_trait_method(cx, match_expr, &paths::ITERATOR)
- && lhs_constructor.ident.name == sym!(Some)
+ && lhs_constructor.ident.name == sym::Some
&& (pat_args.is_empty()
|| !is_refutable(cx, &pat_args[0])
&& !is_used_inside(cx, iter_expr, &arms[0].body)
@@ -985,13 +985,13 @@
_ => false,
};
- is_slice || is_type_diagnostic_item(cx, ty, sym!(vec_type)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type))
+ is_slice || is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type))
}
fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
if_chain! {
if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
- if method.ident.name == sym!(clone);
+ if method.ident.name == sym::clone;
if args.len() == 1;
if let Some(arg) = args.get(0);
then { arg } else { expr }
@@ -1355,7 +1355,7 @@
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 is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym!(vec_type));
+ 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))
@@ -1736,7 +1736,7 @@
/// Checks for `for` loops over `Option`s and `Result`s.
fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
let ty = cx.typeck_results().expr_ty(arg);
- if is_type_diagnostic_item(cx, ty, sym!(option_type)) {
+ if is_type_diagnostic_item(cx, ty, sym::option_type) {
span_lint_and_help(
cx,
FOR_LOOPS_OVER_FALLIBLES,
@@ -1753,7 +1753,7 @@
snippet(cx, arg.span, "_")
),
);
- } else if is_type_diagnostic_item(cx, ty, sym!(result_type)) {
+ } else if is_type_diagnostic_item(cx, ty, sym::result_type) {
span_lint_and_help(
cx,
FOR_LOOPS_OVER_FALLIBLES,
@@ -2186,8 +2186,8 @@
if_chain! {
// a range index op
if let ExprKind::MethodCall(ref meth, _, ref args, _) = expr.kind;
- if (meth.ident.name == sym!(index) && match_trait_method(self.cx, expr, &paths::INDEX))
- || (meth.ident.name == sym!(index_mut) && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
+ if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX))
+ || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
if !self.check(&args[1], &args[0], expr);
then { return }
}
@@ -2333,7 +2333,7 @@
// will allow further borrows afterwards
let ty = cx.typeck_results().expr_ty(e);
is_iterable_array(ty, cx) ||
- is_type_diagnostic_item(cx, ty, sym!(vec_type)) ||
+ is_type_diagnostic_item(cx, ty, sym::vec_type) ||
match_type(cx, ty, &paths::LINKED_LIST) ||
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) ||
is_type_diagnostic_item(cx, ty, sym!(hashset_type)) ||
@@ -2890,7 +2890,7 @@
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
then {
let ty = cx.typeck_results().node_type(ty.hir_id);
- if is_type_diagnostic_item(cx, ty, sym!(vec_type)) ||
+ if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
match_type(cx, ty, &paths::BTREEMAP) ||
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) {
@@ -3001,7 +3001,14 @@
IterFunctionKind::IntoIter => String::new(),
IterFunctionKind::Len => String::from(".count()"),
IterFunctionKind::IsEmpty => String::from(".next().is_none()"),
- IterFunctionKind::Contains(span) => format!(".any(|x| x == {})", snippet(cx, *span, "..")),
+ IterFunctionKind::Contains(span) => {
+ let s = snippet(cx, *span, "..");
+ if let Some(stripped) = s.strip_prefix('&') {
+ format!(".any(|x| x == {})", stripped)
+ } else {
+ format!(".any(|x| x == *{})", s)
+ }
+ },
}
}
fn get_suggestion_text(&self) -> &'static str {
diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs
index 9c62382..a1450b0 100644
--- a/clippy_lints/src/manual_non_exhaustive.rs
+++ b/clippy_lints/src/manual_non_exhaustive.rs
@@ -5,7 +5,7 @@
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::Span;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
@@ -83,9 +83,9 @@
}
fn is_doc_hidden(attr: &Attribute) -> bool {
- attr.has_name(sym!(doc))
+ attr.has_name(sym::doc)
&& match attr.meta_item_list() {
- Some(l) => attr::list_contains_name(&l, sym!(hidden)),
+ Some(l) => attr::list_contains_name(&l, sym::hidden),
None => false,
}
}
@@ -102,7 +102,7 @@
"this seems like a manual implementation of the non-exhaustive pattern",
|diag| {
if_chain! {
- if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive)));
+ if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
let header_span = cx.sess.source_map().span_until_char(item.span, '{');
if let Some(snippet) = snippet_opt(cx, header_span);
then {
@@ -154,7 +154,7 @@
"this seems like a manual implementation of the non-exhaustive pattern",
|diag| {
if_chain! {
- if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive)));
+ if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
let header_span = find_header_span(cx, item, data);
if let Some(snippet) = snippet_opt(cx, header_span);
then {
diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs
new file mode 100644
index 0000000..c99d2e3
--- /dev/null
+++ b/clippy_lints/src/manual_ok_or.rs
@@ -0,0 +1,99 @@
+use crate::utils::{
+ indent_of, is_type_diagnostic_item, match_qpath, paths, reindent_multiline, snippet_opt, span_lint_and_sugg,
+};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{def, Expr, ExprKind, PatKind, QPath};
+use rustc_lint::LintContext;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// **What it does:**
+ /// Finds patterns that reimplement `Option::ok_or`.
+ ///
+ /// **Why is this bad?**
+ /// Concise code helps focusing on behavior instead of boilerplate.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Examples:**
+ /// ```rust
+ /// let foo: Option<i32> = None;
+ /// foo.map_or(Err("error"), |v| Ok(v));
+ ///
+ /// let foo: Option<i32> = None;
+ /// foo.map_or(Err("error"), |v| Ok(v));
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let foo: Option<i32> = None;
+ /// foo.ok_or("error");
+ /// ```
+ pub MANUAL_OK_OR,
+ pedantic,
+ "finds patterns that can be encoded more concisely with `Option::ok_or`"
+}
+
+declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]);
+
+impl LateLintPass<'_> for ManualOkOr {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
+ if in_external_macro(cx.sess(), scrutinee.span) {
+ return;
+ }
+
+ if_chain! {
+ if let ExprKind::MethodCall(method_segment, _, args, _) = scrutinee.kind;
+ if method_segment.ident.name == sym!(map_or);
+ if args.len() == 3;
+ let method_receiver = &args[0];
+ let ty = cx.typeck_results().expr_ty(method_receiver);
+ if is_type_diagnostic_item(cx, ty, sym!(option_type));
+ let or_expr = &args[1];
+ if is_ok_wrapping(cx, &args[2]);
+ if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
+ if match_qpath(err_path, &paths::RESULT_ERR);
+ if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span);
+ if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
+ if let Some(indent) = indent_of(cx, scrutinee.span);
+ then {
+ let reindented_err_arg_snippet =
+ reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
+ span_lint_and_sugg(
+ cx,
+ MANUAL_OK_OR,
+ scrutinee.span,
+ "this pattern reimplements `Option::ok_or`",
+ "replace with",
+ format!(
+ "{}.ok_or({})",
+ method_receiver_snippet,
+ reindented_err_arg_snippet
+ ),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+}
+
+fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
+ if let ExprKind::Path(ref qpath) = map_expr.kind {
+ if match_qpath(qpath, &paths::RESULT_OK) {
+ return true;
+ }
+ }
+ if_chain! {
+ if let ExprKind::Closure(_, _, body_id, ..) = map_expr.kind;
+ let body = cx.tcx.hir().body(body_id);
+ if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
+ if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
+ if match_qpath(ok_path, &paths::RESULT_OK);
+ if let ExprKind::Path(QPath::Resolved(_, ok_arg_path)) = ok_arg.kind;
+ if let def::Res::Local(ok_arg_path_id) = ok_arg_path.res;
+ then { param_id == ok_arg_path_id } else { false }
+ }
+}
diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs
index 22aa37e..9e2c6c7 100644
--- a/clippy_lints/src/manual_unwrap_or.rs
+++ b/clippy_lints/src/manual_unwrap_or.rs
@@ -8,6 +8,7 @@
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:**
@@ -97,9 +98,9 @@
if_chain! {
if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind;
let ty = cx.typeck_results().expr_ty(scrutinee);
- if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)) {
+ if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym::option_type) {
Some(Case::Option)
- } else if utils::is_type_diagnostic_item(cx, ty, sym!(result_type)) {
+ } else if utils::is_type_diagnostic_item(cx, ty, sym::result_type) {
Some(Case::Result)
} else {
None
diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs
index 6d1c2ff..034cd99 100644
--- a/clippy_lints/src/map_clone.rs
+++ b/clippy_lints/src/map_clone.rs
@@ -10,7 +10,7 @@
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Ident;
-use rustc_span::Span;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests
@@ -53,7 +53,7 @@
if args.len() == 2;
if method.ident.as_str() == "map";
let ty = cx.typeck_results().expr_ty(&args[0]);
- if is_type_diagnostic_item(cx, ty, sym!(option_type)) || match_trait_method(cx, e, &paths::ITERATOR);
+ if is_type_diagnostic_item(cx, ty, sym::option_type) || match_trait_method(cx, e, &paths::ITERATOR);
if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind;
let closure_body = cx.tcx.hir().body(body_id);
let closure_expr = remove_blocks(&closure_body.value);
diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs
index d4c2e66..6b78238 100644
--- a/clippy_lints/src/map_identity.rs
+++ b/clippy_lints/src/map_identity.rs
@@ -7,6 +7,7 @@
use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
@@ -65,8 +66,8 @@
if args.len() == 2 && method.ident.as_str() == "map";
let caller_ty = cx.typeck_results().expr_ty(&args[0]);
if match_trait_method(cx, expr, &paths::ITERATOR)
- || is_type_diagnostic_item(cx, caller_ty, sym!(result_type))
- || is_type_diagnostic_item(cx, caller_ty, sym!(option_type));
+ || is_type_diagnostic_item(cx, caller_ty, sym::result_type)
+ || is_type_diagnostic_item(cx, caller_ty, sym::option_type);
then {
Some(args)
} else {
diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs
index 076ef23..e50d11a 100644
--- a/clippy_lints/src/map_unit_fn.rs
+++ b/clippy_lints/src/map_unit_fn.rs
@@ -6,6 +6,7 @@
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for usage of `option.map(f)` where f is a function
@@ -206,9 +207,9 @@
let var_arg = &map_args[0];
let (map_type, variant, lint) =
- if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym!(option_type)) {
+ if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::option_type) {
("Option", "Some", OPTION_MAP_UNIT_FN)
- } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym!(result_type)) {
+ } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::result_type) {
("Result", "Ok", RESULT_MAP_UNIT_FN)
} else {
return;
diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs
index 331b6c6..086dae9 100644
--- a/clippy_lints/src/match_on_vec_items.rs
+++ b/clippy_lints/src/match_on_vec_items.rs
@@ -5,6 +5,7 @@
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`.
@@ -90,7 +91,7 @@
fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let ty = cx.typeck_results().expr_ty(expr);
let ty = ty.peel_refs();
- is_type_diagnostic_item(cx, ty, sym!(vec_type))
+ is_type_diagnostic_item(cx, ty, sym::vec_type)
}
fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs
index b1a4e06..c6dca54 100644
--- a/clippy_lints/src/matches.rs
+++ b/clippy_lints/src/matches.rs
@@ -1,5 +1,4 @@
use crate::consts::{constant, miri_to_const, Constant};
-use crate::utils::paths;
use crate::utils::sugg::Sugg;
use crate::utils::usage::is_unused;
use crate::utils::{
@@ -8,8 +7,10 @@
snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
span_lint_and_then,
};
+use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
+use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::def::CtorKind;
use rustc_hir::{
@@ -18,10 +19,12 @@
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, TyS};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{Span, Spanned};
+use rustc_span::{sym, Symbol};
use std::cmp::Ordering;
+use std::collections::hash_map::Entry;
use std::collections::Bound;
declare_clippy_lint! {
@@ -36,7 +39,6 @@
/// ```rust
/// # fn bar(stool: &str) {}
/// # let x = Some("abc");
- ///
/// // Bad
/// match x {
/// Some(ref foo) => bar(foo),
@@ -239,7 +241,6 @@
/// ```rust
/// # enum Foo { A(usize), B(usize) }
/// # let x = Foo::B(1);
- ///
/// // Bad
/// match x {
/// Foo::A(_) => {},
@@ -477,6 +478,47 @@
"a match that could be written with the matches! macro"
}
+declare_clippy_lint! {
+ /// **What it does:** Checks for `match` with identical arm bodies.
+ ///
+ /// **Why is this bad?** This is probably a copy & paste error. If arm bodies
+ /// are the same on purpose, you can factor them
+ /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
+ ///
+ /// **Known problems:** False positive possible with order dependent `match`
+ /// (see issue
+ /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
+ ///
+ /// **Example:**
+ /// ```rust,ignore
+ /// match foo {
+ /// Bar => bar(),
+ /// Quz => quz(),
+ /// Baz => bar(), // <= oops
+ /// }
+ /// ```
+ ///
+ /// This should probably be
+ /// ```rust,ignore
+ /// match foo {
+ /// Bar => bar(),
+ /// Quz => quz(),
+ /// Baz => baz(), // <= fixed
+ /// }
+ /// ```
+ ///
+ /// or if the original code was not a typo:
+ /// ```rust,ignore
+ /// match foo {
+ /// Bar | Baz => bar(), // <= shows the intent better
+ /// Quz => quz(),
+ /// }
+ /// ```
+ pub MATCH_SAME_ARMS,
+ pedantic,
+ "`match` with identical arm bodies"
+}
+
#[derive(Default)]
pub struct Matches {
infallible_destructuring_match_linted: bool,
@@ -497,7 +539,8 @@
INFALLIBLE_DESTRUCTURING_MATCH,
REST_PAT_IN_FULLY_BOUND_STRUCTS,
REDUNDANT_PATTERN_MATCHING,
- MATCH_LIKE_MATCHES_MACRO
+ MATCH_LIKE_MATCHES_MACRO,
+ MATCH_SAME_ARMS,
]);
impl<'tcx> LateLintPass<'tcx> for Matches {
@@ -507,7 +550,9 @@
}
redundant_pattern_match::check(cx, expr);
- check_match_like_matches(cx, expr);
+ if !check_match_like_matches(cx, expr) {
+ lint_match_arms(cx, expr);
+ }
if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
check_single_match(cx, ex, arms, expr);
@@ -617,7 +662,7 @@
}
} else {
// not a block, don't lint
- return;
+ return;
};
let ty = cx.typeck_results().expr_ty(ex);
@@ -795,7 +840,7 @@
fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
- if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) {
+ 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 {
let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
@@ -1065,32 +1110,47 @@
}
/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
-fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind {
match match_source {
MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false),
MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true),
- _ => return,
+ _ => false,
}
+ } else {
+ false
}
}
/// Lint a `match` or desugared `if let` for replacement by `matches!`
-fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) {
+fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) -> bool {
if_chain! {
- if arms.len() == 2;
+ if arms.len() >= 2;
if cx.typeck_results().expr_ty(expr).is_bool();
- if is_wild(&arms[1].pat);
- if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared);
- if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared);
- if first != second;
+ if let Some((b1_arm, b0_arms)) = arms.split_last();
+ if let Some(b0) = find_bool_lit(&b0_arms[0].body.kind, desugared);
+ if let Some(b1) = find_bool_lit(&b1_arm.body.kind, desugared);
+ if is_wild(&b1_arm.pat);
+ if b0 != b1;
+ let if_guard = &b0_arms[0].guard;
+ if if_guard.is_none() || b0_arms.len() == 1;
+ if b0_arms[1..].iter()
+ .all(|arm| {
+ find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) &&
+ arm.guard.is_none()
+ });
then {
let mut applicability = Applicability::MachineApplicable;
-
- let pat_and_guard = if let Some(Guard::If(g)) = arms[0].guard {
- format!("{} if {}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), snippet_with_applicability(cx, g.span, "..", &mut applicability))
+ let pat = {
+ use itertools::Itertools as _;
+ b0_arms.iter()
+ .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability))
+ .join(" | ")
+ };
+ let pat_and_guard = if let Some(Guard::If(g)) = if_guard {
+ format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
} else {
- format!("{}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability))
+ pat
};
span_lint_and_sugg(
cx,
@@ -1100,12 +1160,15 @@
"try this",
format!(
"{}matches!({}, {})",
- if first { "" } else { "!" },
+ if b0 { "" } else { "!" },
snippet_with_applicability(cx, ex.span, "..", &mut applicability),
pat_and_guard,
),
applicability,
- )
+ );
+ true
+ } else {
+ false
}
}
}
@@ -1446,6 +1509,7 @@
use rustc_errors::Applicability;
use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath};
use rustc_lint::LateContext;
+ use rustc_span::sym;
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
@@ -1489,7 +1553,7 @@
if_chain! {
if keyword == "while";
if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
- if method_path.ident.name == sym!(next);
+ if method_path.ident.name == sym::next;
if match_trait_method(cx, op, &paths::ITERATOR);
then {
return;
@@ -1659,3 +1723,119 @@
],)
);
}
+
+/// Implementation of `MATCH_SAME_ARMS`.
+fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+ fn same_bindings<'tcx>(lhs: &FxHashMap<Symbol, Ty<'tcx>>, rhs: &FxHashMap<Symbol, Ty<'tcx>>) -> bool {
+ lhs.len() == rhs.len()
+ && lhs
+ .iter()
+ .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty)))
+ }
+
+ if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind {
+ let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
+ let mut h = SpanlessHash::new(cx);
+ h.hash_expr(&arm.body);
+ h.finish()
+ };
+
+ let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
+ let min_index = usize::min(lindex, rindex);
+ let max_index = usize::max(lindex, rindex);
+
+ // Arms with a guard are ignored, those can’t always be merged together
+ // This is also the case for arms in-between each there is an arm with a guard
+ (min_index..=max_index).all(|index| arms[index].guard.is_none()) &&
+ SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) &&
+ // all patterns should have the same bindings
+ same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat))
+ };
+
+ let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
+ for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
+ span_lint_and_then(
+ cx,
+ MATCH_SAME_ARMS,
+ j.body.span,
+ "this `match` has identical arm bodies",
+ |diag| {
+ diag.span_note(i.body.span, "same as this");
+
+ // Note: this does not use `span_suggestion` on purpose:
+ // there is no clean way
+ // to remove the other arm. Building a span and suggest to replace it to ""
+ // makes an even more confusing error message. Also in order not to make up a
+ // span for the whole pattern, the suggestion is only shown when there is only
+ // one pattern. The user should know about `|` if they are already using it…
+
+ let lhs = snippet(cx, i.pat.span, "<pat1>");
+ let rhs = snippet(cx, j.pat.span, "<pat2>");
+
+ if let PatKind::Wild = j.pat.kind {
+ // if the last arm is _, then i could be integrated into _
+ // note that i.pat cannot be _, because that would mean that we're
+ // hiding all the subsequent arms, and rust won't compile
+ diag.span_note(
+ i.body.span,
+ &format!(
+ "`{}` has the same arm body as the `_` wildcard, consider removing it",
+ lhs
+ ),
+ );
+ } else {
+ diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
+ }
+ },
+ );
+ }
+ }
+}
+
+/// Returns the list of bindings in a pattern.
+fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap<Symbol, Ty<'tcx>> {
+ fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap<Symbol, Ty<'tcx>>) {
+ match pat.kind {
+ PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
+ PatKind::TupleStruct(_, pats, _) => {
+ for pat in pats {
+ bindings_impl(cx, pat, map);
+ }
+ },
+ PatKind::Binding(.., ident, ref as_pat) => {
+ if let Entry::Vacant(v) = map.entry(ident.name) {
+ v.insert(cx.typeck_results().pat_ty(pat));
+ }
+ if let Some(ref as_pat) = *as_pat {
+ bindings_impl(cx, as_pat, map);
+ }
+ },
+ PatKind::Or(fields) | PatKind::Tuple(fields, _) => {
+ for pat in fields {
+ bindings_impl(cx, pat, map);
+ }
+ },
+ PatKind::Struct(_, fields, _) => {
+ for pat in fields {
+ bindings_impl(cx, &pat.pat, map);
+ }
+ },
+ PatKind::Slice(lhs, ref mid, rhs) => {
+ for pat in lhs {
+ bindings_impl(cx, pat, map);
+ }
+ if let Some(ref mid) = *mid {
+ bindings_impl(cx, mid, map);
+ }
+ for pat in rhs {
+ bindings_impl(cx, pat, map);
+ }
+ },
+ PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (),
+ }
+ }
+
+ let mut result = FxHashMap::default();
+ bindings_impl(cx, pat, &mut result);
+ result
+}
diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs
index 5dae7ef..c83b6f2 100644
--- a/clippy_lints/src/methods/inefficient_to_string.rs
+++ b/clippy_lints/src/methods/inefficient_to_string.rs
@@ -7,6 +7,7 @@
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
+use rustc_span::sym;
/// Checks for the `INEFFICIENT_TO_STRING` lint
pub fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) {
@@ -50,7 +51,7 @@
return true;
}
- if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
+ if is_type_diagnostic_item(cx, ty, sym::string_type) {
return true;
}
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index c0824ba..7186656 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -32,8 +32,7 @@
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,
+ span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq,
};
declare_clippy_lint! {
@@ -799,40 +798,6 @@
}
declare_clippy_lint! {
- /// **What it does:** Checks for getting the inner pointer of a temporary
- /// `CString`.
- ///
- /// **Why is this bad?** The inner pointer of a `CString` is only valid as long
- /// as the `CString` is alive.
- ///
- /// **Known problems:** None.
- ///
- /// **Example:**
- /// ```rust
- /// # use std::ffi::CString;
- /// # fn call_some_ffi_func(_: *const i8) {}
- /// #
- /// let c_str = CString::new("foo").unwrap().as_ptr();
- /// unsafe {
- /// call_some_ffi_func(c_str);
- /// }
- /// ```
- /// Here `c_str` points to a freed address. The correct use would be:
- /// ```rust
- /// # use std::ffi::CString;
- /// # fn call_some_ffi_func(_: *const i8) {}
- /// #
- /// let c_str = CString::new("foo").unwrap();
- /// unsafe {
- /// call_some_ffi_func(c_str.as_ptr());
- /// }
- /// ```
- pub TEMPORARY_CSTRING_AS_PTR,
- correctness,
- "getting the inner pointer of a temporary `CString`"
-}
-
-declare_clippy_lint! {
/// **What it does:** Checks for calling `.step_by(0)` on iterators which panics.
///
/// **Why is this bad?** This very much looks like an oversight. Use `panic!()` instead if you
@@ -1325,8 +1290,8 @@
}
declare_clippy_lint! {
- /// **What it does:** Warns when using `push_str` with a single-character string literal,
- /// and `push` with a `char` would work fine.
+ /// **What it does:** Warns when using `push_str`/`insert_str` with a single-character string literal
+ /// where `push`/`insert` with a `char` would work fine.
///
/// **Why is this bad?** It's less clear that we are pushing a single character.
///
@@ -1335,16 +1300,18 @@
/// **Example:**
/// ```rust
/// let mut string = String::new();
+ /// string.insert_str(0, "R");
/// string.push_str("R");
/// ```
/// Could be written as
/// ```rust
/// let mut string = String::new();
+ /// string.insert(0, 'R');
/// string.push('R');
/// ```
- pub SINGLE_CHAR_PUSH_STR,
+ pub SINGLE_CHAR_ADD_STR,
style,
- "`push_str()` used with a single-character string literal as parameter"
+ "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
}
declare_clippy_lint! {
@@ -1383,6 +1350,60 @@
"using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
}
+declare_clippy_lint! {
+ /// **What it does:** Checks for usage of `_.map(_).collect::<Result<(),_>()`.
+ ///
+ /// **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic.
+ ///
+ /// **Known problems:** None
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// (0..3).try_for_each(|t| Err(t));
+ /// ```
+ pub MAP_COLLECT_RESULT_UNIT,
+ style,
+ "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator`
+ /// trait.
+ ///
+ /// **Why is this bad?** It is recommended style to use collect. See
+ /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// use std::iter::FromIterator;
+ ///
+ /// let five_fives = std::iter::repeat(5).take(5);
+ ///
+ /// let v = Vec::from_iter(five_fives);
+ ///
+ /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let five_fives = std::iter::repeat(5).take(5);
+ ///
+ /// let v: Vec<i32> = five_fives.collect();
+ ///
+ /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
+ /// ```
+ pub FROM_ITER_INSTEAD_OF_COLLECT,
+ style,
+ "use `.collect()` instead of `::from_iter()`"
+}
+
declare_lint_pass!(Methods => [
UNWRAP_USED,
EXPECT_USED,
@@ -1404,9 +1425,8 @@
INEFFICIENT_TO_STRING,
NEW_RET_NO_SELF,
SINGLE_CHAR_PATTERN,
- SINGLE_CHAR_PUSH_STR,
+ SINGLE_CHAR_ADD_STR,
SEARCH_IS_SOME,
- TEMPORARY_CSTRING_AS_PTR,
FILTER_NEXT,
SKIP_WHILE_NEXT,
FILTER_MAP,
@@ -1433,6 +1453,8 @@
FILETYPE_IS_FILE,
OPTION_AS_REF_DEREF,
UNNECESSARY_LAZY_EVALUATIONS,
+ MAP_COLLECT_RESULT_UNIT,
+ FROM_ITER_INSTEAD_OF_COLLECT,
]);
impl<'tcx> LateLintPass<'tcx> for Methods {
@@ -1490,7 +1512,6 @@
lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1])
},
["extend", ..] => lint_extend(cx, expr, arg_lists[0]),
- ["as_ptr", "unwrap" | "expect"] => lint_cstring_as_ptr(cx, expr, &arg_lists[1][0], &arg_lists[0][0]),
["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false),
["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true),
["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]),
@@ -1515,10 +1536,18 @@
["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"),
+ ["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]),
_ => {},
}
match expr.kind {
+ hir::ExprKind::Call(ref func, ref args) => {
+ if let hir::ExprKind::Path(path) = &func.kind {
+ if match_qpath(path, &["from_iter"]) {
+ lint_from_iter(cx, expr, args);
+ }
+ }
+ },
hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => {
lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
@@ -1535,6 +1564,8 @@
if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
if match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
lint_single_char_push_string(cx, expr, args);
+ } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) {
+ lint_single_char_insert_string(cx, expr, args);
}
}
@@ -1691,7 +1722,7 @@
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if_chain! {
if !in_external_macro(cx.tcx.sess, item.span);
- if item.ident.name == sym!(new);
+ if item.ident.name == sym::new;
if let TraitItemKind::Fn(_, _) = item.kind;
let ret_ty = return_ty(cx, item.hir_id);
let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty();
@@ -1748,7 +1779,7 @@
"try this",
format!(
"{}.unwrap_or_default()",
- snippet_with_applicability(cx, self_expr.span, "_", &mut applicability)
+ snippet_with_applicability(cx, self_expr.span, "..", &mut applicability)
),
applicability,
);
@@ -1781,7 +1812,7 @@
_ => (),
}
- if is_type_diagnostic_item(cx, ty, sym!(vec_type)) {
+ if is_type_diagnostic_item(cx, ty, sym::vec_type) {
return;
}
}
@@ -1878,11 +1909,11 @@
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr,
hir::ExprKind::MethodCall(method_name, _, call_args, _) => {
if call_args.len() == 1
- && (method_name.ident.name == sym!(as_str) || method_name.ident.name == sym!(as_ref))
+ && (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 = arg_type.peel_refs();
- *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type))
+ *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::string_type)
}
{
&call_args[0]
@@ -1900,7 +1931,7 @@
// converted to string.
fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
let arg_ty = cx.typeck_results().expr_ty(arg);
- if is_type_diagnostic_item(cx, arg_ty, sym!(string_type)) {
+ if is_type_diagnostic_item(cx, arg_ty, sym::string_type) {
return false;
}
if let ty::Ref(_, ty, ..) = arg_ty.kind() {
@@ -1987,9 +2018,9 @@
}
let receiver_type = cx.typeck_results().expr_ty_adjusted(&args[0]);
- let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym!(option_type)) {
+ let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::option_type) {
"||"
- } else if is_type_diagnostic_item(cx, receiver_type, sym!(result_type)) {
+ } else if is_type_diagnostic_item(cx, receiver_type, sym::result_type) {
"|_|"
} else {
return;
@@ -2155,7 +2186,7 @@
return;
};
- let snippet = snippet_with_macro_callsite(cx, arg.span, "_");
+ let snippet = snippet_with_macro_callsite(cx, arg.span, "..");
span_lint_and_sugg(
cx,
@@ -2176,7 +2207,7 @@
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)) {
+ } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
"&"
} else {
return;
@@ -2191,9 +2222,9 @@
"try this",
format!(
"{}.push_str({}{})",
- snippet_with_applicability(cx, args[0].span, "_", &mut applicability),
+ snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
ref_str,
- snippet_with_applicability(cx, target.span, "_", &mut applicability)
+ snippet_with_applicability(cx, target.span, "..", &mut applicability)
),
applicability,
);
@@ -2202,34 +2233,14 @@
fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
- if is_type_diagnostic_item(cx, obj_ty, sym!(string_type)) {
+ if is_type_diagnostic_item(cx, obj_ty, sym::string_type) {
lint_string_extend(cx, expr, args);
}
}
-fn lint_cstring_as_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, source: &hir::Expr<'_>, unwrap: &hir::Expr<'_>) {
- if_chain! {
- let source_type = cx.typeck_results().expr_ty(source);
- if let ty::Adt(def, substs) = source_type.kind();
- if cx.tcx.is_diagnostic_item(sym!(result_type), def.did);
- if match_type(cx, substs.type_at(0), &paths::CSTRING);
- then {
- span_lint_and_then(
- cx,
- TEMPORARY_CSTRING_AS_PTR,
- expr.span,
- "you are getting the inner pointer of a temporary `CString`",
- |diag| {
- diag.note("that pointer will be invalid outside this expression");
- diag.span_help(unwrap.span, "assign the `CString` to a variable to extend its lifetime");
- });
- }
- }
-}
-
fn lint_iter_cloned_collect<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) {
if_chain! {
- if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym!(vec_type));
+ if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type);
if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0]));
if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite());
@@ -2382,7 +2393,7 @@
);
}
}
- } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym!(vec_type))
+ } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym::vec_type)
|| matches!(
&cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(),
ty::Array(_, _)
@@ -2415,7 +2426,7 @@
let mut_str = if is_mut { "_mut" } else { "" };
let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])).is_some() {
"slice"
- } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vec_type)) {
+ } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vec_type) {
"Vec"
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vecdeque_type)) {
"VecDeque"
@@ -2460,7 +2471,7 @@
let mut applicability = Applicability::MachineApplicable;
let expr_ty = cx.typeck_results().expr_ty(&get_args[0]);
let get_args_str = if get_args.len() > 1 {
- snippet_with_applicability(cx, get_args[1].span, "_", &mut applicability)
+ snippet_with_applicability(cx, get_args[1].span, "..", &mut applicability)
} else {
return; // not linting on a .get().unwrap() chain or variant
};
@@ -2468,7 +2479,7 @@
let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() {
needs_ref = get_args_str.parse::<usize>().is_ok();
"slice"
- } else if is_type_diagnostic_item(cx, expr_ty, sym!(vec_type)) {
+ } else if is_type_diagnostic_item(cx, expr_ty, sym::vec_type) {
needs_ref = get_args_str.parse::<usize>().is_ok();
"Vec"
} else if is_type_diagnostic_item(cx, expr_ty, sym!(vecdeque_type)) {
@@ -2520,7 +2531,7 @@
format!(
"{}{}[{}]",
borrow_str,
- snippet_with_applicability(cx, get_args[0].span, "_", &mut applicability),
+ snippet_with_applicability(cx, get_args[0].span, "..", &mut applicability),
get_args_str
),
applicability,
@@ -2536,7 +2547,7 @@
cx,
ITER_SKIP_NEXT,
expr.span.trim_start(caller.span).unwrap(),
- "called `skip(x).next()` on an iterator",
+ "called `skip(..).next()` on an iterator",
"use `nth` instead",
hint,
Applicability::MachineApplicable,
@@ -2554,7 +2565,7 @@
match ty.kind() {
ty::Slice(_) => true,
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
- ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)),
+ ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type),
ty::Array(_, size) => size
.try_eval_usize(cx.tcx, cx.param_env)
.map_or(false, |size| size < 32),
@@ -2564,7 +2575,7 @@
}
if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind {
- if path.ident.name == sym!(iter) && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) {
+ if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) {
Some(&args[0])
} else {
None
@@ -2589,9 +2600,9 @@
fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) {
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)) {
+ let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) {
Some((UNWRAP_USED, "an Option", "None"))
- } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) {
+ } else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) {
Some((UNWRAP_USED, "a Result", "Err"))
} else {
None
@@ -2641,7 +2652,7 @@
fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Expr<'_>]) {
if_chain! {
// lint if the caller of `ok()` is a `Result`
- if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&ok_args[0]), sym!(result_type));
+ if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&ok_args[0]), sym::result_type);
let result_type = cx.typeck_results().expr_ty(&ok_args[0]);
if let Some(error_type) = get_error_type(cx, result_type);
if has_debug_impl(error_type, cx);
@@ -2671,7 +2682,7 @@
_ => map_closure_ty.fn_sig(cx.tcx),
};
let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&map_closure_sig.output());
- is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type))
+ is_type_diagnostic_item(cx, map_closure_return_ty, sym::option_type)
},
_ => false,
};
@@ -2697,7 +2708,7 @@
}
// lint if caller of `.map().flatten()` is an Option
- if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) {
+ if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) {
let func_snippet = snippet(cx, map_args[1].span, "..");
let hint = format!(".and_then({})", func_snippet);
span_lint_and_sugg(
@@ -2721,8 +2732,8 @@
unwrap_args: &'tcx [hir::Expr<'_>],
) -> bool {
// lint if the caller of `map()` is an `Option`
- let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type));
- let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(result_type));
+ let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type);
+ let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type);
if is_option || is_result {
// Don't make a suggestion that may fail to compile due to mutably borrowing
@@ -2739,11 +2750,11 @@
// lint message
let msg = if is_option {
- "called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling \
- `map_or_else(g, f)` instead"
+ "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling \
+ `map_or_else(<g>, <f>)` instead"
} else {
- "called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling \
- `.map_or_else(g, f)` instead"
+ "called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling \
+ `.map_or_else(<g>, <f>)` instead"
};
// get snippets for args to map() and unwrap_or_else()
let map_snippet = snippet(cx, map_args[1].span, "..");
@@ -2753,16 +2764,15 @@
let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt();
if same_span && !multiline {
- span_lint_and_note(
+ let var_snippet = snippet(cx, map_args[0].span, "..");
+ span_lint_and_sugg(
cx,
MAP_UNWRAP_OR,
expr.span,
msg,
- None,
- &format!(
- "replace `map({0}).unwrap_or_else({1})` with `map_or_else({1}, {0})`",
- map_snippet, unwrap_snippet,
- ),
+ "try this",
+ format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet),
+ Applicability::MachineApplicable,
);
return true;
} else if same_span && multiline {
@@ -2776,8 +2786,8 @@
/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_or_args: &'tcx [hir::Expr<'_>]) {
- let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym!(option_type));
- let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym!(result_type));
+ let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::option_type);
+ let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::result_type);
// There are two variants of this `map_or` lint:
// (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>`
@@ -2809,8 +2819,8 @@
if is_option {
let self_snippet = snippet(cx, map_or_args[0].span, "..");
let func_snippet = snippet(cx, map_or_args[2].span, "..");
- let msg = "called `map_or(None, f)` on an `Option` value. This can be done more directly by calling \
- `and_then(f)` instead";
+ let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
+ `and_then(..)` instead";
(
OPTION_MAP_OR_NONE,
msg,
@@ -2848,18 +2858,20 @@
fn lint_filter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) {
// lint if caller of `.filter().next()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
- let msg = "called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling \
- `.find(p)` instead.";
+ let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
+ `.find(..)` instead.";
let filter_snippet = snippet(cx, filter_args[1].span, "..");
if filter_snippet.lines().count() <= 1 {
+ let iter_snippet = snippet(cx, filter_args[0].span, "..");
// add note if not multi-line
- span_lint_and_note(
+ span_lint_and_sugg(
cx,
FILTER_NEXT,
expr.span,
msg,
- None,
- &format!("replace `filter({0}).next()` with `find({0})`", filter_snippet),
+ "try this",
+ format!("{}.find({})", iter_snippet, filter_snippet),
+ Applicability::MachineApplicable,
);
} else {
span_lint(cx, FILTER_NEXT, expr.span, msg);
@@ -2879,9 +2891,9 @@
cx,
SKIP_WHILE_NEXT,
expr.span,
- "called `skip_while(p).next()` on an `Iterator`",
+ "called `skip_while(<p>).next()` on an `Iterator`",
None,
- "this is more succinctly expressed by calling `.find(!p)` instead",
+ "this is more succinctly expressed by calling `.find(!<p>)` instead",
);
}
}
@@ -2895,7 +2907,7 @@
) {
// lint if caller of `.filter().map()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
- let msg = "called `filter(p).map(q)` on an `Iterator`";
+ let msg = "called `filter(..).map(..)` on an `Iterator`";
let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead";
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
}
@@ -2904,17 +2916,19 @@
/// lint use of `filter_map().next()` for `Iterators`
fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) {
if match_trait_method(cx, expr, &paths::ITERATOR) {
- let msg = "called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling \
- `.find_map(p)` instead.";
+ let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
+ `.find_map(..)` instead.";
let filter_snippet = snippet(cx, filter_args[1].span, "..");
if filter_snippet.lines().count() <= 1 {
- span_lint_and_note(
+ let iter_snippet = snippet(cx, filter_args[0].span, "..");
+ span_lint_and_sugg(
cx,
FILTER_MAP_NEXT,
expr.span,
msg,
- None,
- &format!("replace `filter_map({0}).next()` with `find_map({0})`", filter_snippet),
+ "try this",
+ format!("{}.find_map({})", iter_snippet, filter_snippet),
+ Applicability::MachineApplicable,
);
} else {
span_lint(cx, FILTER_MAP_NEXT, expr.span, msg);
@@ -2931,7 +2945,7 @@
) {
// lint if caller of `.filter().map()` is an Iterator
if match_trait_method(cx, &map_args[0], &paths::ITERATOR) {
- let msg = "called `find(p).map(q)` on an `Iterator`";
+ let msg = "called `find(..).map(..)` on an `Iterator`";
let hint = "this is more succinctly expressed by calling `.find_map(..)` instead";
span_lint_and_help(cx, FIND_MAP, expr.span, msg, None, hint);
}
@@ -2946,7 +2960,7 @@
) {
// lint if caller of `.filter().map()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
- let msg = "called `filter_map(p).map(q)` on an `Iterator`";
+ let msg = "called `filter_map(..).map(..)` on an `Iterator`";
let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead";
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
}
@@ -2961,7 +2975,7 @@
) {
// lint if caller of `.filter().flat_map()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
- let msg = "called `filter(p).flat_map(q)` on an `Iterator`";
+ let msg = "called `filter(..).flat_map(..)` on an `Iterator`";
let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
and filtering by returning `iter::empty()`";
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
@@ -2977,7 +2991,7 @@
) {
// lint if caller of `.filter_map().flat_map()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
- let msg = "called `filter_map(p).flat_map(q)` on an `Iterator`";
+ let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`";
let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
and filtering by returning `iter::empty()`";
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
@@ -3131,7 +3145,7 @@
if arg_char.len() == 1;
if let hir::ExprKind::Path(ref qpath) = fun.kind;
if let Some(segment) = single_segment_path(qpath);
- if segment.ident.name == sym!(Some);
+ if segment.ident.name == sym::Some;
then {
let mut applicability = Applicability::MachineApplicable;
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
@@ -3148,9 +3162,9 @@
"like this",
format!("{}{}.{}({})",
if info.eq { "" } else { "!" },
- snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability),
+ snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
suggest,
- snippet_with_applicability(cx, arg_char[0].span, "_", &mut applicability)),
+ snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
applicability,
);
@@ -3197,7 +3211,7 @@
"like this",
format!("{}{}.{}('{}')",
if info.eq { "" } else { "!" },
- snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability),
+ snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
suggest,
c),
applicability,
@@ -3233,7 +3247,7 @@
if let hir::ExprKind::Lit(lit) = &arg.kind;
if let ast::LitKind::Str(r, style) = lit.node;
let string = r.as_str();
- if string.len() == 1;
+ if string.chars().count() == 1;
then {
let snip = snippet_with_applicability(cx, arg.span, &string, applicability);
let ch = if let ast::StrStyle::Raw(nhash) = style {
@@ -3272,11 +3286,12 @@
fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let mut applicability = Applicability::MachineApplicable;
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) {
- let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
+ let base_string_snippet =
+ snippet_with_applicability(cx, args[0].span.source_callsite(), "..", &mut applicability);
let sugg = format!("{}.push({})", base_string_snippet, extension_string);
span_lint_and_sugg(
cx,
- SINGLE_CHAR_PUSH_STR,
+ SINGLE_CHAR_ADD_STR,
expr.span,
"calling `push_str()` using a single-character string literal",
"consider using `push` with a character literal",
@@ -3286,6 +3301,26 @@
}
}
+/// lint for length-1 `str`s as argument for `insert_str`
+fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
+ let mut applicability = Applicability::MachineApplicable;
+ if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) {
+ let base_string_snippet =
+ snippet_with_applicability(cx, args[0].span.source_callsite(), "_", &mut applicability);
+ let pos_arg = snippet_with_applicability(cx, args[1].span, "..", &mut applicability);
+ let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string);
+ span_lint_and_sugg(
+ cx,
+ SINGLE_CHAR_ADD_STR,
+ expr.span,
+ "calling `insert_str()` using a single-character string literal",
+ "consider using `insert` with a character literal",
+ sugg,
+ applicability,
+ );
+ }
+}
+
/// Checks for the `USELESS_ASREF` lint.
fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) {
// when we get here, we've already checked that the call name is "as_ref" or "as_mut"
@@ -3315,7 +3350,7 @@
expr.span,
&format!("this call to `{}` does nothing", call_name),
"try this",
- snippet_with_applicability(cx, recvr.span, "_", &mut applicability).to_string(),
+ snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(),
applicability,
);
}
@@ -3406,7 +3441,7 @@
let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not);
let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]);
- if !is_type_diagnostic_item(cx, option_ty, sym!(option_type)) {
+ if !is_type_diagnostic_item(cx, option_ty, sym::option_type) {
return;
}
@@ -3501,10 +3536,46 @@
}
}
+fn lint_map_collect(
+ cx: &LateContext<'_>,
+ expr: &hir::Expr<'_>,
+ map_args: &[hir::Expr<'_>],
+ collect_args: &[hir::Expr<'_>],
+) {
+ if_chain! {
+ // called on Iterator
+ if let [map_expr] = collect_args;
+ if match_trait_method(cx, map_expr, &paths::ITERATOR);
+ // return of collect `Result<(),_>`
+ let collect_ret_ty = cx.typeck_results().expr_ty(expr);
+ if is_type_diagnostic_item(cx, collect_ret_ty, sym::result_type);
+ if let ty::Adt(_, substs) = collect_ret_ty.kind();
+ if let Some(result_t) = substs.types().next();
+ if result_t.is_unit();
+ // get parts for snippet
+ if let [iter, map_fn] = map_args;
+ then {
+ span_lint_and_sugg(
+ cx,
+ MAP_COLLECT_RESULT_UNIT,
+ expr.span,
+ "`.map().collect()` can be replaced with `.try_for_each()`",
+ "try this",
+ format!(
+ "{}.try_for_each({})",
+ snippet(cx, iter.span, ".."),
+ snippet(cx, map_fn.span, "..")
+ ),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+}
+
/// Given a `Result<T, E>` type, return its error type (`E`).
fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
match ty.kind() {
- ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym!(result_type)) => substs.types().nth(1),
+ ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::result_type) => substs.types().nth(1),
_ => None,
}
}
@@ -3826,6 +3897,28 @@
span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg);
}
+fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
+ let ty = cx.typeck_results().expr_ty(expr);
+ let arg_ty = cx.typeck_results().expr_ty(&args[0]);
+
+ let from_iter_id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap();
+ let iter_id = get_trait_def_id(cx, &paths::ITERATOR).unwrap();
+
+ if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]) {
+ // `expr` implements `FromIterator` trait
+ let iter_expr = snippet(cx, args[0].span, "..");
+ span_lint_and_sugg(
+ cx,
+ FROM_ITER_INSTEAD_OF_COLLECT,
+ expr.span,
+ "usage of `FromIterator::from_iter`",
+ "use `.collect()` instead of `::from_iter()`",
+ format!("{}.collect()", iter_expr),
+ Applicability::MaybeIncorrect,
+ );
+ }
+}
+
fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
expected.constness == actual.constness
&& expected.unsafety == actual.unsafety
diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs
index 95fa28e..7763fd5 100644
--- a/clippy_lints/src/methods/option_map_unwrap_or.rs
+++ b/clippy_lints/src/methods/option_map_unwrap_or.rs
@@ -7,7 +7,7 @@
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::source_map::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::{sym, Symbol};
use super::MAP_UNWRAP_OR;
@@ -20,7 +20,7 @@
map_span: Span,
) {
// lint if the caller of `map()` is an `Option`
- if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) {
+ if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) {
if !is_copy(cx, cx.typeck_results().expr_ty(&unwrap_args[1])) {
// Do not lint if the `map` argument uses identifiers in the `map`
// argument that are also used in the `unwrap_or` argument
@@ -53,15 +53,15 @@
// lint message
// comparing the snippet from source to raw text ("None") below is safe
// because we already have checked the type.
- let arg = if unwrap_snippet == "None" { "None" } else { "a" };
+ let arg = if unwrap_snippet == "None" { "None" } else { "<a>" };
let unwrap_snippet_none = unwrap_snippet == "None";
let suggest = if unwrap_snippet_none {
- "and_then(f)"
+ "and_then(<f>)"
} else {
- "map_or(a, f)"
+ "map_or(<a>, <f>)"
};
let msg = &format!(
- "called `map(f).unwrap_or({})` on an `Option` value. \
+ "called `map(<f>).unwrap_or({})` on an `Option` value. \
This can be done more directly by calling `{}` instead",
arg, suggest
);
diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs
index 08b3eab..cde8998 100644
--- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs
+++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs
@@ -3,6 +3,7 @@
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
+use rustc_span::sym;
use super::UNNECESSARY_LAZY_EVALUATIONS;
@@ -14,8 +15,8 @@
args: &'tcx [hir::Expr<'_>],
simplify_using: &str,
) {
- let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type));
- let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type));
+ let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::option_type);
+ let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::result_type);
if is_option || is_result {
if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {
diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs
index 909e79f..308e920 100644
--- a/clippy_lints/src/misc.rs
+++ b/clippy_lints/src/misc.rs
@@ -7,6 +7,7 @@
StmtKind, TyKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::hygiene::DesugaringKind;
@@ -271,13 +272,16 @@
k: FnKind<'tcx>,
decl: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
- _: Span,
+ span: Span,
_: HirId,
) {
if let FnKind::Closure(_) = k {
// Does not apply to closures
return;
}
+ if in_external_macro(cx.tcx.sess, span) {
+ return;
+ }
for arg in iter_input_pats(decl, body) {
if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind {
span_lint(
@@ -293,13 +297,16 @@
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if_chain! {
+ if !in_external_macro(cx.tcx.sess, stmt.span);
if let StmtKind::Local(ref local) = stmt.kind;
if let PatKind::Binding(an, .., name, None) = local.pat.kind;
if let Some(ref init) = local.init;
if !higher::is_from_for_desugar(local);
then {
if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut {
- let sugg_init = if init.span.from_expansion() {
+ // use the macro callsite when the init span (but not the whole local span)
+ // comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];`
+ let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() {
Sugg::hir_with_macro_callsite(cx, init, "..")
} else {
Sugg::hir(cx, init, "..")
@@ -310,7 +317,7 @@
("", sugg_init.addr())
};
let tyopt = if let Some(ref ty) = local.ty {
- format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "_"))
+ format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, ".."))
} else {
String::new()
};
@@ -326,7 +333,7 @@
"try",
format!(
"let {name}{tyopt} = {initref};",
- name=snippet(cx, name.span, "_"),
+ name=snippet(cx, name.span, ".."),
tyopt=tyopt,
initref=initref,
),
diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs
index 9cb1cfb..5bc45c8 100644
--- a/clippy_lints/src/misc_early.rs
+++ b/clippy_lints/src/misc_early.rs
@@ -231,7 +231,6 @@
/// ```rust
/// # struct TupleStruct(u32, u32, u32);
/// # let t = TupleStruct(1, 2, 3);
- ///
/// // Bad
/// match t {
/// TupleStruct(0, .., _) => (),
diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs
index 813f9c4..009e3d8 100644
--- a/clippy_lints/src/missing_doc.rs
+++ b/clippy_lints/src/missing_doc.rs
@@ -14,6 +14,7 @@
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Warns if there is missing doc for any documentable item
@@ -105,10 +106,10 @@
fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, attrs: &'tcx [ast::Attribute]) {
let doc_hidden = self.doc_hidden()
|| attrs.iter().any(|attr| {
- attr.has_name(sym!(doc))
+ attr.has_name(sym::doc)
&& match attr.meta_item_list() {
None => false,
- Some(l) => attr::list_contains_name(&l[..], sym!(hidden)),
+ Some(l) => attr::list_contains_name(&l[..], sym::hidden),
}
});
self.doc_hidden_stack.push(doc_hidden);
@@ -128,7 +129,7 @@
hir::ItemKind::Enum(..) => "an enum",
hir::ItemKind::Fn(..) => {
// ignore main()
- if it.ident.name == sym!(main) {
+ if it.ident.name == sym::main {
let def_id = it.hir_id.owner;
let def_key = cx.tcx.hir().def_key(def_id);
if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) {
diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs
index 3eae45b..53abe60 100644
--- a/clippy_lints/src/missing_inline.rs
+++ b/clippy_lints/src/missing_inline.rs
@@ -4,6 +4,7 @@
use rustc_lint::{self, LateContext, LateLintPass, LintContext};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** it lints if an exported function, method, trait method with default impl,
@@ -57,7 +58,7 @@
}
fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
- let has_inline = attrs.iter().any(|a| a.has_name(sym!(inline)));
+ let has_inline = attrs.iter().any(|a| a.has_name(sym::inline));
if !has_inline {
span_lint(
cx,
diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs
index b71d549..405c21d 100644
--- a/clippy_lints/src/needless_borrow.rs
+++ b/clippy_lints/src/needless_borrow.rs
@@ -10,6 +10,7 @@
use rustc_middle::ty;
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for address of operations (`&`) that are going to
@@ -112,7 +113,7 @@
}
fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) {
- if item.attrs.iter().any(|a| a.has_name(sym!(automatically_derived))) {
+ if item.attrs.iter().any(|a| a.has_name(sym::automatically_derived)) {
debug_assert!(self.derived_item.is_none());
self.derived_item = Some(item.hir_id);
}
diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs
index 7e933c6..5c92590 100644
--- a/clippy_lints/src/needless_pass_by_value.rs
+++ b/clippy_lints/src/needless_pass_by_value.rs
@@ -13,7 +13,7 @@
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TypeFoldable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::Span;
+use rustc_span::{sym, Span};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::misc::can_type_implement_copy;
@@ -204,12 +204,12 @@
let deref_span = spans_need_deref.get(&canonical_id);
if_chain! {
- if is_type_diagnostic_item(cx, ty, sym!(vec_type));
+ if is_type_diagnostic_item(cx, ty, sym::vec_type);
if let Some(clone_spans) =
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]);
if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind;
if let Some(elem_ty) = path.segments.iter()
- .find(|seg| seg.ident.name == sym!(Vec))
+ .find(|seg| seg.ident.name == sym::Vec)
.and_then(|ps| ps.args.as_ref())
.map(|params| params.args.iter().find_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty),
@@ -243,7 +243,7 @@
}
}
- if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
+ if is_type_diagnostic_item(cx, ty, sym::string_type) {
if let Some(clone_spans) =
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
diag.span_suggestion(
@@ -302,7 +302,7 @@
/// Functions marked with these attributes must have the exact signature.
fn requires_exact_signature(attrs: &[Attribute]) -> bool {
attrs.iter().any(|attr| {
- [sym!(proc_macro), sym!(proc_macro_attribute), sym!(proc_macro_derive)]
+ [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
.iter()
.any(|&allow| attr.has_name(allow))
})
diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs
index 28d1322..68fdd0e 100644
--- a/clippy_lints/src/new_without_default.rs
+++ b/clippy_lints/src/new_without_default.rs
@@ -9,6 +9,7 @@
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{Ty, TyS};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for types with a `fn new() -> Self` method and no
@@ -91,7 +92,7 @@
// impl of `Default`
return;
}
- if sig.decl.inputs.is_empty() && name == sym!(new) && cx.access_levels.is_reachable(id) {
+ if sig.decl.inputs.is_empty() && name == sym::new && cx.access_levels.is_reachable(id) {
let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id));
let self_ty = cx.tcx.type_of(self_def_id);
if_chain! {
diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs
index 603440c..485888f 100644
--- a/clippy_lints/src/non_expressive_names.rs
+++ b/clippy_lints/src/non_expressive_names.rs
@@ -7,6 +7,7 @@
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
+use rustc_span::sym;
use rustc_span::symbol::{Ident, Symbol};
use std::cmp::Ordering;
@@ -384,7 +385,7 @@
}
fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
- if !attrs.iter().any(|attr| attr.has_name(sym!(test))) {
+ if !attrs.iter().any(|attr| attr.has_name(sym::test)) {
let mut visitor = SimilarNamesLocalVisitor {
names: Vec::new(),
cx,
diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs
index eb7624b..681dbce 100644
--- a/clippy_lints/src/option_if_let_else.rs
+++ b/clippy_lints/src/option_if_let_else.rs
@@ -8,6 +8,7 @@
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:**
@@ -66,7 +67,7 @@
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"
- && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym!(result_type))
+ && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym::result_type)
} else {
false
}
diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs
index 4077aba..72dfccc 100644
--- a/clippy_lints/src/panic_in_result_fn.rs
+++ b/clippy_lints/src/panic_in_result_fn.rs
@@ -5,7 +5,7 @@
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::Span;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result.
@@ -40,7 +40,7 @@
hir_id: hir::HirId,
) {
if !matches!(fn_kind, FnKind::Closure(_))
- && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type))
+ && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type)
{
lint_impl_body(cx, span, body);
}
diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs
index 19d355e..ceecc8d 100644
--- a/clippy_lints/src/partialeq_ne_impl.rs
+++ b/clippy_lints/src/partialeq_ne_impl.rs
@@ -3,6 +3,7 @@
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for manual re-implementations of `PartialEq::ne`.
@@ -39,7 +40,7 @@
if trait_ref.path.res.def_id() == eq_trait;
then {
for impl_item in impl_items {
- if impl_item.ident.name == sym!(ne) {
+ if impl_item.ident.name == sym::ne {
span_lint_hir(
cx,
PARTIALEQ_NE_IMPL,
diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs
index 28816c3..030650c 100644
--- a/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/clippy_lints/src/pass_by_ref_or_value.rs
@@ -10,7 +10,7 @@
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
+use rustc_span::{sym, Span};
use rustc_target::abi::LayoutOf;
use rustc_target::spec::abi::Abi;
use rustc_target::spec::Target;
@@ -230,8 +230,8 @@
}
for a in attrs {
if let Some(meta_items) = a.meta_item_list() {
- if a.has_name(sym!(proc_macro_derive))
- || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always)))
+ if a.has_name(sym::proc_macro_derive)
+ || (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always))
{
return;
}
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index 6b1c848..dcb643a 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -15,7 +15,7 @@
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
-use rustc_span::MultiSpan;
+use rustc_span::{sym, MultiSpan};
use std::borrow::Cow;
declare_clippy_lint! {
@@ -181,7 +181,7 @@
}
if let ty::Ref(_, ty, Mutability::Not) = ty.kind() {
- if is_type_diagnostic_item(cx, ty, sym!(vec_type)) {
+ if is_type_diagnostic_item(cx, ty, sym::vec_type) {
let mut ty_snippet = None;
if_chain! {
if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind;
@@ -225,7 +225,7 @@
},
);
}
- } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
+ } else if is_type_diagnostic_item(cx, ty, sym::string_type) {
if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
span_lint_and_then(
cx,
diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs
index 0a2d1b5..e099680 100644
--- a/clippy_lints/src/ptr_offset_with_cast.rs
+++ b/clippy_lints/src/ptr_offset_with_cast.rs
@@ -3,6 +3,7 @@
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
use std::fmt;
declare_clippy_lint! {
@@ -92,7 +93,7 @@
) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> {
if let ExprKind::MethodCall(ref path_segment, _, ref args, _) = expr.kind {
if is_expr_ty_raw_ptr(cx, &args[0]) {
- if path_segment.ident.name == sym!(offset) {
+ if path_segment.ident.name == sym::offset {
return Some((&args[0], &args[1], Method::Offset));
}
if path_segment.ident.name == sym!(wrapping_offset) {
diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs
index dbc676a..d9b280b 100644
--- a/clippy_lints/src/question_mark.rs
+++ b/clippy_lints/src/question_mark.rs
@@ -4,6 +4,7 @@
use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
use crate::utils::sugg::Sugg;
use crate::utils::{
@@ -143,7 +144,7 @@
fn is_option(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expression);
- is_type_diagnostic_item(cx, expr_ty, sym!(option_type))
+ is_type_diagnostic_item(cx, expr_ty, sym::option_type)
}
fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs
index de54711..79e9a56 100644
--- a/clippy_lints/src/ranges.rs
+++ b/clippy_lints/src/ranges.rs
@@ -7,6 +7,7 @@
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{Span, Spanned};
+use rustc_span::sym;
use rustc_span::symbol::Ident;
use std::cmp::Ordering;
@@ -304,7 +305,7 @@
if_chain! {
// `.iter()` call
if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = *iter;
- if iter_path.ident.name == sym!(iter);
+ if iter_path.ident.name == sym::iter;
// range expression in `.zip()` call: `0..x.len()`
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
if is_integer_const(cx, start, 0);
diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs
index b4502c6..b4a9804 100644
--- a/clippy_lints/src/redundant_clone.rs
+++ b/clippy_lints/src/redundant_clone.rs
@@ -17,7 +17,9 @@
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};
+use rustc_span::sym;
use std::convert::TryFrom;
+use std::ops::ControlFlow;
macro_rules! unwrap_or_continue {
($x:expr) => {
@@ -114,7 +116,7 @@
let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
|| match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD)
|| (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD)
- && is_type_diagnostic_item(cx, arg_ty, sym!(string_type)));
+ && is_type_diagnostic_item(cx, arg_ty, sym::string_type));
let from_deref = !from_borrow
&& (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF)
@@ -517,7 +519,10 @@
self.possible_borrower.add(borrowed.local, lhs);
},
other => {
- if !ContainsRegion.visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) {
+ if ContainsRegion
+ .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty)
+ .is_continue()
+ {
return;
}
rvalue_locals(other, |rhs| {
@@ -539,7 +544,7 @@
// If the call returns something with lifetimes,
// let's conservatively assume the returned value contains lifetime of all the arguments.
// For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
- if !ContainsRegion.visit_ty(&self.body.local_decls[*dest].ty) {
+ if ContainsRegion.visit_ty(&self.body.local_decls[*dest].ty).is_continue() {
return;
}
@@ -558,8 +563,8 @@
struct ContainsRegion;
impl TypeVisitor<'_> for ContainsRegion {
- fn visit_region(&mut self, _: ty::Region<'_>) -> bool {
- true
+ fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow<()> {
+ ControlFlow::BREAK
}
}
diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs
new file mode 100644
index 0000000..a914a77
--- /dev/null
+++ b/clippy_lints/src/ref_option_ref.rs
@@ -0,0 +1,66 @@
+use crate::utils::{last_path_segment, snippet, span_lint_and_sugg};
+use rustc_hir::{GenericArg, Mutability, Ty, TyKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for usage of `&Option<&T>`.
+ ///
+ /// **Why is this bad?** Since `&` is Copy, it's useless to have a
+ /// reference on `Option<&T>`.
+ ///
+ /// **Known problems:** It may be irrevelent to use this lint on
+ /// public API code as it will make a breaking change to apply it.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust,ignore
+ /// let x: &Option<&u32> = &Some(&0u32);
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// let x: Option<&u32> = Some(&0u32);
+ /// ```
+ pub REF_OPTION_REF,
+ pedantic,
+ "use `Option<&T>` instead of `&Option<&T>`"
+}
+
+declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]);
+
+impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
+ fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
+ if_chain! {
+ if let TyKind::Rptr(_, ref mut_ty) = ty.kind;
+ if mut_ty.mutbl == Mutability::Not;
+ if let TyKind::Path(ref qpath) = &mut_ty.ty.kind;
+ let last = last_path_segment(qpath);
+ if let Some(res) = last.res;
+ if let Some(def_id) = res.opt_def_id();
+
+ if cx.tcx.is_diagnostic_item(sym!(option_type), def_id);
+ if let Some(ref params) = last_path_segment(qpath).args ;
+ if !params.parenthesized;
+ if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
+ GenericArg::Type(inner_ty) => Some(inner_ty),
+ _ => None,
+ });
+ if let TyKind::Rptr(_, _) = inner_ty.kind;
+
+ then {
+ span_lint_and_sugg(
+ cx,
+ REF_OPTION_REF,
+ ty.span,
+ "since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`",
+ "try",
+ format!("Option<{}>", &snippet(cx, inner_ty.span, "..")),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+}
diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs
index ae60135..d34e744 100644
--- a/clippy_lints/src/repeat_once.rs
+++ b/clippy_lints/src/repeat_once.rs
@@ -5,6 +5,7 @@
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types.
@@ -65,7 +66,7 @@
format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)),
Applicability::MachineApplicable,
);
- } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
+ } else if is_type_diagnostic_item(cx, ty, sym::string_type) {
span_lint_and_sugg(
cx,
REPEAT_ONCE,
diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs
index a6e4252..7f4913a 100644
--- a/clippy_lints/src/returns.rs
+++ b/clippy_lints/src/returns.rs
@@ -9,6 +9,7 @@
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
+use rustc_span::sym;
use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then};
@@ -141,7 +142,7 @@
}
fn attr_is_cfg(attr: &Attribute) -> bool {
- attr.meta_item_list().is_some() && attr.has_name(sym!(cfg))
+ attr.meta_item_list().is_some() && attr.has_name(sym::cfg)
}
fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {
diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs
index 3783bd7..0dd2da9 100644
--- a/clippy_lints/src/strings.rs
+++ b/clippy_lints/src/strings.rs
@@ -4,6 +4,7 @@
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
+use rustc_span::sym;
use if_chain::if_chain;
@@ -154,7 +155,7 @@
}
fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
- is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), 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 {
diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs
index 54b38d9..386987e 100644
--- a/clippy_lints/src/swap.rs
+++ b/clippy_lints/src/swap.rs
@@ -8,6 +8,7 @@
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for manual swapping.
@@ -197,7 +198,7 @@
if matches!(ty.kind(), ty::Slice(_))
|| matches!(ty.kind(), ty::Array(_, _))
- || is_type_diagnostic_item(cx, ty, sym!(vec_type))
+ || is_type_diagnostic_item(cx, ty, sym::vec_type)
|| is_type_diagnostic_item(cx, ty, sym!(vecdeque_type))
{
return Slice::Swappable(lhs1, idx1, idx2);
diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs
index 3e747ec..6f6b699 100644
--- a/clippy_lints/src/try_err.rs
+++ b/clippy_lints/src/try_err.rs
@@ -9,6 +9,7 @@
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for usages of `Err(x)?`.
@@ -133,7 +134,7 @@
fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
if_chain! {
if let ty::Adt(_, subst) = ty.kind();
- if is_type_diagnostic_item(cx, ty, sym!(result_type));
+ if is_type_diagnostic_item(cx, ty, sym::result_type);
let err_ty = subst.type_at(1);
then {
Some(err_ty)
@@ -151,7 +152,7 @@
let ready_ty = subst.type_at(0);
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
- if cx.tcx.is_diagnostic_item(sym!(result_type), ready_def.did);
+ if cx.tcx.is_diagnostic_item(sym::result_type, ready_def.did);
let err_ty = ready_subst.type_at(1);
then {
@@ -170,11 +171,11 @@
let ready_ty = subst.type_at(0);
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
- if cx.tcx.is_diagnostic_item(sym!(option_type), ready_def.did);
+ if cx.tcx.is_diagnostic_item(sym::option_type, ready_def.did);
let some_ty = ready_subst.type_at(0);
if let ty::Adt(some_def, some_subst) = some_ty.kind();
- if cx.tcx.is_diagnostic_item(sym!(result_type), some_def.did);
+ if cx.tcx.is_diagnostic_item(sym::result_type, some_def.did);
let err_ty = some_subst.type_at(1);
then {
diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs
index 6a33aaa..c7d82da 100644
--- a/clippy_lints/src/types.rs
+++ b/clippy_lints/src/types.rs
@@ -10,9 +10,9 @@
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, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind,
- TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
+ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId,
+ ImplItem, ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt,
+ StmtKind, SyntheticTyParamKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map;
@@ -522,7 +522,7 @@
);
return; // don't recurse into the type
}
- } else if cx.tcx.is_diagnostic_item(sym!(vec_type), def_id) {
+ } else if cx.tcx.is_diagnostic_item(sym::vec_type, def_id) {
if_chain! {
// Get the _ part of Vec<_>
if let Some(ref last) = last_path_segment(qpath).args;
@@ -559,7 +559,7 @@
return; // don't recurse into the type
}
}
- } else if cx.tcx.is_diagnostic_item(sym!(option_type), def_id) {
+ } else if cx.tcx.is_diagnostic_item(sym::option_type, def_id) {
if match_type_parameter(cx, qpath, &paths::OPTION).is_some() {
span_lint(
cx,
@@ -678,17 +678,30 @@
// details.
return;
}
+
+ // When trait objects or opaque types have lifetime or auto-trait bounds,
+ // we need to add parentheses to avoid a syntax error due to its ambiguity.
+ // Originally reported as the issue #3128.
+ let inner_snippet = snippet(cx, inner.span, "..");
+ let suggestion = match &inner.kind {
+ TyKind::TraitObject(bounds, lt_bound) if bounds.len() > 1 || !lt_bound.is_elided() => {
+ format!("&{}({})", ltopt, &inner_snippet)
+ },
+ TyKind::Path(qpath)
+ if get_bounds_if_impl_trait(cx, qpath, inner.hir_id)
+ .map_or(false, |bounds| bounds.len() > 1) =>
+ {
+ format!("&{}({})", ltopt, &inner_snippet)
+ },
+ _ => format!("&{}{}", ltopt, &inner_snippet),
+ };
span_lint_and_sugg(
cx,
BORROWED_BOX,
hir_ty.span,
"you seem to be trying to use `&Box<T>`. Consider using just `&T`",
"try",
- format!(
- "&{}{}",
- ltopt,
- &snippet(cx, inner.span, "..")
- ),
+ suggestion,
// 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.
@@ -721,6 +734,21 @@
false
}
+fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> {
+ if_chain! {
+ if let Some(did) = qpath_res(cx, qpath, id).opt_def_id();
+ if let Some(node) = cx.tcx.hir().get_if_local(did);
+ if let Node::GenericParam(generic_param) = node;
+ if let GenericParamKind::Type { synthetic, .. } = generic_param.kind;
+ if synthetic == Some(SyntheticTyParamKind::ImplTrait);
+ then {
+ Some(generic_param.bounds)
+ } else {
+ None
+ }
+ }
+}
+
declare_clippy_lint! {
/// **What it does:** Checks for binding a unit value.
///
@@ -1582,7 +1610,7 @@
if names.is_empty() {
return false;
}
- if names[0] == sym!(libc) || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) {
+ if names[0] == sym::libc || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) {
return true;
}
}
@@ -2749,7 +2777,7 @@
}
if match_path(ty_path, &paths::HASHMAP) {
- if method.ident.name == sym!(new) {
+ if method.ident.name == sym::new {
self.suggestions
.insert(e.span, "HashMap::default()".to_string());
} else if method.ident.name == sym!(with_capacity) {
@@ -2762,7 +2790,7 @@
);
}
} else if match_path(ty_path, &paths::HASHSET) {
- if method.ident.name == sym!(new) {
+ if method.ident.name == sym::new {
self.suggestions
.insert(e.span, "HashSet::default()".to_string());
} else if method.ident.name == sym!(with_capacity) {
diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs
index 1307237..0bccfc1 100644
--- a/clippy_lints/src/unnecessary_sort_by.rs
+++ b/clippy_lints/src/unnecessary_sort_by.rs
@@ -6,6 +6,7 @@
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, subst::GenericArgKind};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
use rustc_span::symbol::Ident;
declare_clippy_lint! {
@@ -175,7 +176,7 @@
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::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym!(vec_type));
+ if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::vec_type);
if let closure_body = cx.tcx.hir().body(*closure_body_id);
if let &[
Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs
index ea4b817..f4a77e5 100644
--- a/clippy_lints/src/unwrap.rs
+++ b/clippy_lints/src/unwrap.rs
@@ -11,6 +11,7 @@
use rustc_middle::ty::Ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.
@@ -92,11 +93,11 @@
invert: bool,
) -> Vec<UnwrapInfo<'tcx>> {
fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool {
- is_type_diagnostic_item(cx, ty, sym!(option_type)) && ["is_some", "is_none"].contains(&method_name)
+ is_type_diagnostic_item(cx, ty, sym::option_type) && ["is_some", "is_none"].contains(&method_name)
}
fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool {
- is_type_diagnostic_item(cx, ty, sym!(result_type)) && ["is_ok", "is_err"].contains(&method_name)
+ is_type_diagnostic_item(cx, ty, sym::result_type) && ["is_ok", "is_err"].contains(&method_name)
}
if let ExprKind::Binary(op, left, right) = &expr.kind {
@@ -168,8 +169,8 @@
if_chain! {
if let ExprKind::MethodCall(ref method_name, _, ref args, _) = expr.kind;
if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].kind;
- if [sym!(unwrap), sym!(unwrap_err)].contains(&method_name.ident.name);
- let call_to_unwrap = method_name.ident.name == sym!(unwrap);
+ if [sym::unwrap, sym!(unwrap_err)].contains(&method_name.ident.name);
+ let call_to_unwrap = method_name.ident.name == sym::unwrap;
if let Some(unwrappable) = self.unwrappables.iter()
.find(|u| u.ident.res == path.res);
// Span contexts should not differ with the conditional branch
diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs
index 0f87972..fde3102 100644
--- a/clippy_lints/src/unwrap_in_result.rs
+++ b/clippy_lints/src/unwrap_in_result.rs
@@ -5,7 +5,7 @@
use rustc_middle::hir::map::Map;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::Span;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()`
@@ -57,8 +57,8 @@
// first check if it's a method or function
if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind;
// checking if its return type is `result` or `option`
- if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type))
- || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type));
+ if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::result_type)
+ || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::option_type);
then {
lint_impl_body(cx, impl_item.span, impl_item);
}
@@ -82,8 +82,8 @@
// check for `expect`
if let Some(arglists) = method_chain_args(expr, &["expect"]) {
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))
+ if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type)
+ || is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type)
{
self.result.push(expr.span);
}
@@ -92,8 +92,8 @@
// check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
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))
+ if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type)
+ || is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type)
{
self.result.push(expr.span);
}
diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs
index 4e4a206..c6194b0 100644
--- a/clippy_lints/src/useless_conversion.rs
+++ b/clippy_lints/src/useless_conversion.rs
@@ -9,6 +9,7 @@
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TyS};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`,`IntoIter` calls
@@ -106,7 +107,7 @@
if_chain! {
let a = cx.typeck_results().expr_ty(e);
let b = cx.typeck_results().expr_ty(&args[0]);
- if is_type_diagnostic_item(cx, a, sym!(result_type));
+ if is_type_diagnostic_item(cx, a, sym::result_type);
if let ty::Adt(_, substs) = a.kind();
if let Some(a_type) = substs.types().next();
if TyS::same_type(a_type, b);
@@ -136,7 +137,7 @@
then {
if_chain! {
if match_def_path(cx, def_id, &paths::TRY_FROM);
- if is_type_diagnostic_item(cx, a, sym!(result_type));
+ if is_type_diagnostic_item(cx, a, sym::result_type);
if let ty::Adt(_, substs) = a.kind();
if let Some(a_type) = substs.types().next();
if TyS::same_type(a_type, b);
diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs
index 8942543..7250de3 100644
--- a/clippy_lints/src/utils/author.rs
+++ b/clippy_lints/src/utils/author.rs
@@ -130,7 +130,7 @@
}
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
- if !has_attr(cx.sess(), stmt.kind.attrs()) {
+ if !has_attr(cx.sess(), stmt.kind.attrs(|id| cx.tcx.hir().item(id.id))) {
return;
}
prelude();
diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs
index c9e639e..e4ad105 100644
--- a/clippy_lints/src/utils/hir_utils.rs
+++ b/clippy_lints/src/utils/hir_utils.rs
@@ -261,14 +261,8 @@
pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
// The == of idents doesn't work with different contexts,
// we have to be explicit about hygiene
- if left.ident.as_str() != right.ident.as_str() {
- return false;
- }
- match (&left.args, &right.args) {
- (&None, &None) => true,
- (&Some(ref l), &Some(ref r)) => self.eq_path_parameters(l, r),
- _ => false,
- }
+ left.ident.as_str() == right.ident.as_str()
+ && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
}
pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs
index 93bd829..4fbfb3b 100644
--- a/clippy_lints/src/utils/inspector.rs
+++ b/clippy_lints/src/utils/inspector.rs
@@ -109,7 +109,7 @@
}
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
- if !has_attr(cx.sess(), stmt.kind.attrs()) {
+ if !has_attr(cx.sess(), stmt.kind.attrs(|id| cx.tcx.hir().item(id.id))) {
return;
}
match stmt.kind {
diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs
index bfe426a..8b59a95 100644
--- a/clippy_lints/src/utils/internal_lints.rs
+++ b/clippy_lints/src/utils/internal_lints.rs
@@ -1,3 +1,4 @@
+use crate::consts::{constant_simple, Constant};
use crate::utils::{
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,
@@ -14,9 +15,11 @@
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_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{Span, Spanned};
use rustc_span::symbol::{Symbol, SymbolStr};
+use rustc_typeck::hir_ty_to_ty;
use std::borrow::{Borrow, Cow};
@@ -222,13 +225,28 @@
///
/// Good:
/// ```rust,ignore
- /// utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))
+ /// 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_clippy_lint! {
+ /// **What it does:**
+ /// Checks the paths module for invalid paths.
+ ///
+ /// **Why is this bad?**
+ /// It indicates a bug in the code.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:** None.
+ pub INVALID_PATHS,
+ internal,
+ "invalid path"
+}
+
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
impl EarlyLintPass for ClippyLintsInternal {
@@ -706,7 +724,7 @@
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),
+ format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
Applicability::MaybeIncorrect,
);
}
@@ -761,3 +779,64 @@
None
}
+
+// This is not a complete resolver for paths. It works on all the paths currently used in the paths
+// module. That's all it does and all it needs to do.
+pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
+ if path_to_res(cx, path).is_some() {
+ return true;
+ }
+
+ // Some implementations can't be found by `path_to_res`, particularly inherent
+ // implementations of native types. Check lang items.
+ let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
+ let lang_items = cx.tcx.lang_items();
+ for lang_item in lang_items.items() {
+ if let Some(def_id) = lang_item {
+ let lang_item_path = cx.get_def_path(*def_id);
+ if path_syms.starts_with(&lang_item_path) {
+ if let [item] = &path_syms[lang_item_path.len()..] {
+ for child in cx.tcx.item_children(*def_id) {
+ if child.ident.name == *item {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ false
+}
+
+declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
+
+impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ let local_def_id = &cx.tcx.parent_module(item.hir_id);
+ let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
+ if_chain! {
+ if mod_name.as_str() == "paths";
+ if let hir::ItemKind::Const(ty, body_id) = item.kind;
+ let ty = hir_ty_to_ty(cx.tcx, ty);
+ if let ty::Array(el_ty, _) = &ty.kind();
+ if let ty::Ref(_, el_ty, _) = &el_ty.kind();
+ if el_ty.is_str();
+ let body = cx.tcx.hir().body(body_id);
+ let typeck_results = cx.tcx.typeck_body(body_id);
+ if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
+ let path: Vec<&str> = path.iter().map(|x| {
+ if let Constant::Str(s) = x {
+ s.as_str()
+ } else {
+ // We checked the type of the constant above
+ unreachable!()
+ }
+ }).collect();
+ if !check_path(cx, &path[..]);
+ then {
+ span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path");
+ }
+ }
+ }
+}
diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs
index 8297b9d..85e7f05 100644
--- a/clippy_lints/src/utils/mod.rs
+++ b/clippy_lints/src/utils/mod.rs
@@ -27,11 +27,14 @@
pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
use std::borrow::Cow;
+use std::collections::hash_map::Entry;
+use std::hash::BuildHasherDefault;
use std::mem;
use if_chain::if_chain;
use rustc_ast::ast::{self, Attribute, LitKind};
use rustc_attr as attr;
+use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
@@ -49,6 +52,7 @@
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::original_sp;
+use rustc_span::sym as rustc_sym;
use rustc_span::symbol::{self, kw, Symbol};
use rustc_span::{BytePos, Pos, Span, DUMMY_SP};
use rustc_target::abi::Integer;
@@ -268,6 +272,7 @@
krate: *krate,
index: CRATE_DEF_INDEX,
};
+ let mut current_item = None;
let mut items = cx.tcx.item_children(krate);
let mut path_it = path.iter().skip(1).peekable();
@@ -277,6 +282,12 @@
None => return None,
};
+ // `get_def_path` seems to generate these empty segments for extern blocks.
+ // We can just ignore them.
+ if segment.is_empty() {
+ continue;
+ }
+
let result = SmallVec::<[_; 8]>::new();
for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() {
if item.ident.name.as_str() == *segment {
@@ -284,10 +295,28 @@
return Some(item.res);
}
+ current_item = Some(item);
items = cx.tcx.item_children(item.res.def_id());
break;
}
}
+
+ // The segment isn't a child_item.
+ // Try to find it under an inherent impl.
+ if_chain! {
+ if path_it.peek().is_none();
+ if let Some(current_item) = current_item;
+ let item_def_id = current_item.res.def_id();
+ if cx.tcx.def_kind(item_def_id) == DefKind::Struct;
+ then {
+ // Bad `find_map` suggestion. See #4193.
+ #[allow(clippy::find_map)]
+ return cx.tcx.inherent_impls(item_def_id).iter()
+ .flat_map(|&impl_def_id| cx.tcx.item_children(impl_def_id))
+ .find(|item| item.ident.name.as_str() == *segment)
+ .map(|item| item.res);
+ }
+ }
}
} else {
None
@@ -946,7 +975,7 @@
/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
/// implementations have.
pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
- attrs.iter().any(|attr| attr.has_name(sym!(automatically_derived)))
+ attrs.iter().any(|attr| attr.has_name(rustc_sym::automatically_derived))
}
/// Remove blocks around an expression.
@@ -1440,6 +1469,41 @@
None
}
+/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
+/// `hash` must be comformed with `eq`
+pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
+where
+ Hash: Fn(&T) -> u64,
+ Eq: Fn(&T, &T) -> bool,
+{
+ if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) {
+ return vec![(&exprs[0], &exprs[1])];
+ }
+
+ let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
+
+ let mut map: FxHashMap<_, Vec<&_>> =
+ FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
+
+ for expr in exprs {
+ match map.entry(hash(expr)) {
+ Entry::Occupied(mut o) => {
+ for o in o.get() {
+ if eq(o, expr) {
+ match_expr_list.push((o, expr));
+ }
+ }
+ o.get_mut().push(expr);
+ },
+ Entry::Vacant(v) => {
+ v.insert(vec![expr]);
+ },
+ }
+ }
+
+ match_expr_list
+}
+
#[macro_export]
macro_rules! unwrap_cargo_metadata {
($cx: ident, $lint: ident, $deps: expr) => {{
diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs
index 52d3c2c..d02603d 100644
--- a/clippy_lints/src/utils/numeric_literal.rs
+++ b/clippy_lints/src/utils/numeric_literal.rs
@@ -1,6 +1,6 @@
use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Radix {
Binary,
Octal,
@@ -11,8 +11,8 @@
impl Radix {
/// Returns a reasonable digit group size for this radix.
#[must_use]
- fn suggest_grouping(&self) -> usize {
- match *self {
+ fn suggest_grouping(self) -> usize {
+ match self {
Self::Binary | Self::Hexadecimal => 4,
Self::Octal | Self::Decimal => 3,
}
diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs
index cd9b92e..1ad8c60 100644
--- a/clippy_lints/src/utils/paths.rs
+++ b/clippy_lints/src/utils/paths.rs
@@ -21,7 +21,6 @@
pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
-pub const CSTRING: [&str; 4] = ["std", "ffi", "c_str", "CString"];
pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
@@ -32,10 +31,10 @@
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
-pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
+pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
pub const EXIT: [&str; 3] = ["std", "process", "exit"];
-pub const F32_EPSILON: [&str; 2] = ["f32", "EPSILON"];
-pub const F64_EPSILON: [&str; 2] = ["f64", "EPSILON"];
+pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
+pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
pub const FILE: [&str; 3] = ["std", "fs", "File"];
pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
@@ -45,22 +44,24 @@
pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"];
pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
+pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
-pub const HASH: [&str; 2] = ["hash", "Hash"];
+pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
+pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
pub const INTO: [&str; 3] = ["core", "convert", "Into"];
pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
-pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"];
+pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
-pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"];
+pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "ManuallyDrop"];
@@ -86,8 +87,8 @@
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
-pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
-pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
+pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"];
+pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"];
pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
@@ -107,7 +108,7 @@
pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
-pub const SERDE_DESERIALIZE: [&str; 2] = ["_serde", "Deserialize"];
+pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "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; 4] = ["core", "slice", "iter", "Iter"];
diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs
index d9d60ff..ff414f7 100644
--- a/clippy_lints/src/write.rs
+++ b/clippy_lints/src/write.rs
@@ -10,8 +10,7 @@
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_parse::parser;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::symbol::Symbol;
-use rustc_span::{BytePos, Span};
+use rustc_span::{sym, BytePos, Span, Symbol};
declare_clippy_lint! {
/// **What it does:** This lint warns when you use `println!("")` to
@@ -224,7 +223,7 @@
.expect("path has at least one segment")
.ident
.name;
- if trait_name == sym!(Debug) {
+ if trait_name == sym::Debug {
self.in_debug_impl = true;
}
}
diff --git a/doc/adding_lints.md b/doc/adding_lints.md
index 2572833..b1dacfc 100644
--- a/doc/adding_lints.md
+++ b/doc/adding_lints.md
@@ -311,7 +311,7 @@
According to [the rustc-dev-guide], the text should be matter of fact and avoid
capitalization and periods, unless multiple sentences are needed.
When code or an identifier must appear in a message or label, it should be
-surrounded with single acute accents \`.
+surrounded with single grave accents \`.
[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs
diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs
index 4bf77da..702f9d8 100644
--- a/src/lintlist/mod.rs
+++ b/src/lintlist/mod.rs
@@ -299,6 +299,13 @@
module: "comparison_chain",
},
Lint {
+ name: "comparison_to_empty",
+ group: "style",
+ desc: "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead",
+ deprecation: None,
+ module: "len_zero",
+ },
+ Lint {
name: "copy_iterator",
group: "pedantic",
desc: "implementing `Iterator` on a `Copy` type",
@@ -352,7 +359,7 @@
group: "pedantic",
desc: "checks for literal calls to `Default::default()`",
deprecation: None,
- module: "default_trait_access",
+ module: "default",
},
Lint {
name: "deprecated_cfg_attr",
@@ -621,6 +628,13 @@
module: "fallible_impl_from",
},
Lint {
+ name: "field_reassign_with_default",
+ group: "style",
+ desc: "binding initialized with Default should have its fields set in the initializer",
+ deprecation: None,
+ module: "default",
+ },
+ Lint {
name: "filetype_is_file",
group: "restriction",
desc: "`FileType::is_file` is not recommended to test for readable file type",
@@ -747,6 +761,13 @@
module: "drop_forget_ref",
},
Lint {
+ name: "from_iter_instead_of_collect",
+ group: "style",
+ desc: "use `.collect()` instead of `::from_iter()`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
name: "future_not_send",
group: "nursery",
desc: "public Futures must be Send",
@@ -1174,6 +1195,13 @@
module: "manual_non_exhaustive",
},
Lint {
+ name: "manual_ok_or",
+ group: "pedantic",
+ desc: "finds patterns that can be encoded more concisely with `Option::ok_or`",
+ deprecation: None,
+ module: "manual_ok_or",
+ },
+ Lint {
name: "manual_range_contains",
group: "style",
desc: "manually reimplementing {`Range`, `RangeInclusive`}`::contains`",
@@ -1223,6 +1251,13 @@
module: "map_clone",
},
Lint {
+ name: "map_collect_result_unit",
+ group: "style",
+ desc: "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`",
+ deprecation: None,
+ module: "methods",
+ },
+ Lint {
name: "map_entry",
group: "perf",
desc: "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`",
@@ -1304,7 +1339,7 @@
group: "pedantic",
desc: "`match` with identical arm bodies",
deprecation: None,
- module: "copies",
+ module: "matches",
},
Lint {
name: "match_single_binding",
@@ -2014,6 +2049,13 @@
module: "reference",
},
Lint {
+ name: "ref_option_ref",
+ group: "pedantic",
+ desc: "use `Option<&T>` instead of `&Option<&T>`",
+ deprecation: None,
+ module: "ref_option_ref",
+ },
+ Lint {
name: "repeat_once",
group: "complexity",
desc: "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` ",
@@ -2133,16 +2175,16 @@
module: "non_expressive_names",
},
Lint {
- name: "single_char_pattern",
- group: "perf",
- desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`",
+ name: "single_char_add_str",
+ group: "style",
+ desc: "`push_str()` or `insert_str()` used with a single-character string literal as parameter",
deprecation: None,
module: "methods",
},
Lint {
- name: "single_char_push_str",
- group: "style",
- desc: "`push_str()` used with a single-character string literal as parameter",
+ name: "single_char_pattern",
+ group: "perf",
+ desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`",
deprecation: None,
module: "methods",
},
@@ -2294,13 +2336,6 @@
module: "temporary_assignment",
},
Lint {
- name: "temporary_cstring_as_ptr",
- group: "correctness",
- desc: "getting the inner pointer of a temporary `CString`",
- deprecation: None,
- module: "methods",
- },
- Lint {
name: "to_digit_is_some",
group: "style",
desc: "`char.is_digit()` is clearer",
@@ -2644,6 +2679,13 @@
module: "unused_unit",
},
Lint {
+ name: "unusual_byte_groupings",
+ group: "style",
+ desc: "binary or hex literals that aren\'t grouped by four",
+ deprecation: None,
+ module: "literal_representation",
+ },
+ Lint {
name: "unwrap_in_result",
group: "restriction",
desc: "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`",
diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs
index 0bbb953..9330386 100644
--- a/tests/ui/auxiliary/macro_rules.rs
+++ b/tests/ui/auxiliary/macro_rules.rs
@@ -56,3 +56,17 @@
option_env!($env).expect($message)
};
}
+
+#[macro_export]
+macro_rules! ref_arg_binding {
+ () => {
+ let ref _y = 42;
+ };
+}
+
+#[macro_export]
+macro_rules! ref_arg_function {
+ () => {
+ fn fun_example(ref _x: usize) {}
+ };
+}
diff --git a/tests/ui/borrow_box.rs b/tests/ui/borrow_box.rs
index 1901de4..b606f77 100644
--- a/tests/ui/borrow_box.rs
+++ b/tests/ui/borrow_box.rs
@@ -3,6 +3,8 @@
#![allow(unused_variables)]
#![allow(dead_code)]
+use std::fmt::Display;
+
pub fn test1(foo: &mut Box<bool>) {
// Although this function could be changed to "&mut bool",
// avoiding the Box, mutable references to boxes are not
@@ -89,6 +91,20 @@
*boxed_slice = data.into_boxed_slice();
}
+// The suggestion should include proper parentheses to avoid a syntax error.
+pub fn test14(_display: &Box<dyn Display>) {}
+pub fn test15(_display: &Box<dyn Display + Send>) {}
+pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
+
+pub fn test17(_display: &Box<impl Display>) {}
+pub fn test18(_display: &Box<impl Display + Send>) {}
+pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
+
+// This exists only to check what happens when parentheses are already present.
+// Even though the current implementation doesn't put extra parentheses,
+// it's fine that unnecessary parentheses appear in the future for some reason.
+pub fn test20(_display: &Box<(dyn Display + Send)>) {}
+
fn main() {
test1(&mut Box::new(false));
test2();
diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr
index b5db691..3eac328 100644
--- a/tests/ui/borrow_box.stderr
+++ b/tests/ui/borrow_box.stderr
@@ -1,5 +1,5 @@
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
- --> $DIR/borrow_box.rs:19:14
+ --> $DIR/borrow_box.rs:21:14
|
LL | let foo: &Box<bool>;
| ^^^^^^^^^^ help: try: `&bool`
@@ -11,16 +11,58 @@
| ^^^^^^^^^^^^^^^^^^^^
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
- --> $DIR/borrow_box.rs:23:10
+ --> $DIR/borrow_box.rs:25:10
|
LL | foo: &'a Box<bool>,
| ^^^^^^^^^^^^^ help: try: `&'a bool`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
- --> $DIR/borrow_box.rs:27:17
+ --> $DIR/borrow_box.rs:29:17
|
LL | fn test4(a: &Box<bool>);
| ^^^^^^^^^^ help: try: `&bool`
-error: aborting due to 3 previous errors
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:95:25
+ |
+LL | pub fn test14(_display: &Box<dyn Display>) {}
+ | ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:96:25
+ |
+LL | pub fn test15(_display: &Box<dyn Display + Send>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:97:29
+ |
+LL | pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:99:25
+ |
+LL | pub fn test17(_display: &Box<impl Display>) {}
+ | ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:100:25
+ |
+LL | pub fn test18(_display: &Box<impl Display + Send>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:101:29
+ |
+LL | pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)`
+
+error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
+ --> $DIR/borrow_box.rs:106:25
+ |
+LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`
+
+error: aborting due to 10 previous errors
diff --git a/tests/ui/comparison_to_empty.fixed b/tests/ui/comparison_to_empty.fixed
new file mode 100644
index 0000000..261024c
--- /dev/null
+++ b/tests/ui/comparison_to_empty.fixed
@@ -0,0 +1,23 @@
+// run-rustfix
+
+#![warn(clippy::comparison_to_empty)]
+
+fn main() {
+ // Disallow comparisons to empty
+ let s = String::new();
+ let _ = s.is_empty();
+ let _ = !s.is_empty();
+
+ let v = vec![0];
+ let _ = v.is_empty();
+ let _ = !v.is_empty();
+
+ // Allow comparisons to non-empty
+ let s = String::new();
+ let _ = s == " ";
+ let _ = s != " ";
+
+ let v = vec![0];
+ let _ = v == [0];
+ let _ = v != [0];
+}
diff --git a/tests/ui/comparison_to_empty.rs b/tests/ui/comparison_to_empty.rs
new file mode 100644
index 0000000..98ddd97
--- /dev/null
+++ b/tests/ui/comparison_to_empty.rs
@@ -0,0 +1,23 @@
+// run-rustfix
+
+#![warn(clippy::comparison_to_empty)]
+
+fn main() {
+ // Disallow comparisons to empty
+ let s = String::new();
+ let _ = s == "";
+ let _ = s != "";
+
+ let v = vec![0];
+ let _ = v == [];
+ let _ = v != [];
+
+ // Allow comparisons to non-empty
+ let s = String::new();
+ let _ = s == " ";
+ let _ = s != " ";
+
+ let v = vec![0];
+ let _ = v == [0];
+ let _ = v != [0];
+}
diff --git a/tests/ui/comparison_to_empty.stderr b/tests/ui/comparison_to_empty.stderr
new file mode 100644
index 0000000..f69d6bd
--- /dev/null
+++ b/tests/ui/comparison_to_empty.stderr
@@ -0,0 +1,28 @@
+error: comparison to empty slice
+ --> $DIR/comparison_to_empty.rs:8:13
+ |
+LL | let _ = s == "";
+ | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()`
+ |
+ = note: `-D clippy::comparison-to-empty` implied by `-D warnings`
+
+error: comparison to empty slice
+ --> $DIR/comparison_to_empty.rs:9:13
+ |
+LL | let _ = s != "";
+ | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()`
+
+error: comparison to empty slice
+ --> $DIR/comparison_to_empty.rs:12:13
+ |
+LL | let _ = v == [];
+ | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()`
+
+error: comparison to empty slice
+ --> $DIR/comparison_to_empty.rs:13:13
+ |
+LL | let _ = v != [];
+ | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/crashes/ice-6250.rs b/tests/ui/crashes/ice-6250.rs
new file mode 100644
index 0000000..c33580f
--- /dev/null
+++ b/tests/ui/crashes/ice-6250.rs
@@ -0,0 +1,16 @@
+// originally from glacier/fixed/77218.rs
+// ice while adjusting...
+
+pub struct Cache {
+ data: Vec<i32>,
+}
+
+pub fn list_data(cache: &Cache, key: usize) {
+ for reference in vec![1, 2, 3] {
+ if
+ /* let */
+ Some(reference) = cache.data.get(key) {
+ unimplemented!()
+ }
+ }
+}
diff --git a/tests/ui/crashes/ice-6250.stderr b/tests/ui/crashes/ice-6250.stderr
new file mode 100644
index 0000000..8241dcd
--- /dev/null
+++ b/tests/ui/crashes/ice-6250.stderr
@@ -0,0 +1,27 @@
+error[E0601]: `main` function not found in crate `ice_6250`
+ --> $DIR/ice-6250.rs:4:1
+ |
+LL | / pub struct Cache {
+LL | | data: Vec<i32>,
+LL | | }
+LL | |
+... |
+LL | | }
+LL | | }
+ | |_^ consider adding a `main` function to `$DIR/ice-6250.rs`
+
+error[E0308]: mismatched types
+ --> $DIR/ice-6250.rs:12:9
+ |
+LL | Some(reference) = cache.data.get(key) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
+ |
+help: you might have meant to use pattern matching
+ |
+LL | let Some(reference) = cache.data.get(key) {
+ | ^^^
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0308, E0601.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/crashes/ice-6251.rs b/tests/ui/crashes/ice-6251.rs
new file mode 100644
index 0000000..6aa779a
--- /dev/null
+++ b/tests/ui/crashes/ice-6251.rs
@@ -0,0 +1,6 @@
+// originally from glacier/fixed/77329.rs
+// assertion failed: `(left == right) ; different DefIds
+
+fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
+ std::iter::empty()
+}
diff --git a/tests/ui/crashes/ice-6251.stderr b/tests/ui/crashes/ice-6251.stderr
new file mode 100644
index 0000000..9a7cf4b
--- /dev/null
+++ b/tests/ui/crashes/ice-6251.stderr
@@ -0,0 +1,43 @@
+error[E0601]: `main` function not found in crate `ice_6251`
+ --> $DIR/ice-6251.rs:4:1
+ |
+LL | / fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
+LL | | std::iter::empty()
+LL | | }
+ | |_^ consider adding a `main` function to `$DIR/ice-6251.rs`
+
+error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
+ --> $DIR/ice-6251.rs:4:45
+ |
+LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
+ | ^ doesn't have a size known at compile-time
+ |
+ = help: the trait `std::marker::Sized` is not implemented for `[u8]`
+ = help: unsized fn params are gated as an unstable feature
+help: function arguments must have a statically known size, borrowed types always have a known size
+ |
+LL | fn bug<T>() -> impl Iterator<Item = [(); { |&x: [u8]| x }]> {
+ | ^
+
+error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
+ --> $DIR/ice-6251.rs:4:54
+ |
+LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
+ | ^ doesn't have a size known at compile-time
+ |
+ = help: the trait `std::marker::Sized` is not implemented for `[u8]`
+ = note: the return type of a function must have a statically known size
+
+error[E0308]: mismatched types
+ --> $DIR/ice-6251.rs:4:44
+ |
+LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
+ | ^^^^^^^^^^^ expected `usize`, found closure
+ |
+ = note: expected type `usize`
+ found closure `[closure@$DIR/ice-6251.rs:4:44: 4:55]`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0277, E0308, E0601.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/crashes/ice-6252.rs b/tests/ui/crashes/ice-6252.rs
new file mode 100644
index 0000000..2e3d9fd
--- /dev/null
+++ b/tests/ui/crashes/ice-6252.rs
@@ -0,0 +1,15 @@
+// originally from glacier fixed/77919.rs
+// encountered errors resolving bounds after type-checking
+
+trait TypeVal<T> {
+ const VAL: T;
+}
+struct Five;
+struct Multiply<N, M> {
+ _n: PhantomData,
+}
+impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
+
+fn main() {
+ [1; <Multiply<Five, Five>>::VAL];
+}
diff --git a/tests/ui/crashes/ice-6252.stderr b/tests/ui/crashes/ice-6252.stderr
new file mode 100644
index 0000000..440973e
--- /dev/null
+++ b/tests/ui/crashes/ice-6252.stderr
@@ -0,0 +1,46 @@
+error[E0412]: cannot find type `PhantomData` in this scope
+ --> $DIR/ice-6252.rs:9:9
+ |
+LL | _n: PhantomData,
+ | ^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this struct
+ |
+LL | use std::marker::PhantomData;
+ |
+
+error[E0412]: cannot find type `VAL` in this scope
+ --> $DIR/ice-6252.rs:11:63
+ |
+LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
+ | - ^^^ not found in this scope
+ | |
+ | help: you might be missing a type parameter: `, VAL`
+
+error[E0046]: not all trait items implemented, missing: `VAL`
+ --> $DIR/ice-6252.rs:11:1
+ |
+LL | const VAL: T;
+ | ------------- `VAL` from trait
+...
+LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation
+
+error: any use of this value will cause an error
+ --> $DIR/ice-6252.rs:5:5
+ |
+LL | const VAL: T;
+ | ^^^^^^^^^^^^^ no MIR body is available for DefId(0:5 ~ ice_6252[317d]::TypeVal::VAL)
+ |
+ = note: `#[deny(const_err)]` on by default
+
+error[E0080]: evaluation of constant value failed
+ --> $DIR/ice-6252.rs:14:9
+ |
+LL | [1; <Multiply<Five, Five>>::VAL];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0046, E0080, E0412.
+For more information about an error, try `rustc --explain E0046`.
diff --git a/tests/ui/crashes/ice-6254.rs b/tests/ui/crashes/ice-6254.rs
new file mode 100644
index 0000000..c19eca4
--- /dev/null
+++ b/tests/ui/crashes/ice-6254.rs
@@ -0,0 +1,15 @@
+// originally from ./src/test/ui/pattern/usefulness/consts-opaque.rs
+// panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())',
+// compiler/rustc_mir_build/src/thir/pattern/_match.rs:2030:5
+
+#[derive(PartialEq)]
+struct Foo(i32);
+const FOO_REF_REF: &&Foo = &&Foo(42);
+
+fn main() {
+ // This used to cause an ICE (https://github.com/rust-lang/rust/issues/78071)
+ match FOO_REF_REF {
+ FOO_REF_REF => {},
+ Foo(_) => {},
+ }
+}
diff --git a/tests/ui/crashes/ice-6254.stderr b/tests/ui/crashes/ice-6254.stderr
new file mode 100644
index 0000000..95ebf23
--- /dev/null
+++ b/tests/ui/crashes/ice-6254.stderr
@@ -0,0 +1,12 @@
+error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
+ --> $DIR/ice-6254.rs:12:9
+ |
+LL | FOO_REF_REF => {},
+ | ^^^^^^^^^^^
+ |
+ = note: `-D indirect-structural-match` implied by `-D warnings`
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #62411 <https://github.com/rust-lang/rust/issues/62411>
+
+error: aborting due to previous error
+
diff --git a/tests/ui/crashes/ice-6255.rs b/tests/ui/crashes/ice-6255.rs
new file mode 100644
index 0000000..bd4a81d
--- /dev/null
+++ b/tests/ui/crashes/ice-6255.rs
@@ -0,0 +1,15 @@
+// originally from rustc ./src/test/ui/macros/issue-78325-inconsistent-resolution.rs
+// inconsistent resolution for a macro
+
+macro_rules! define_other_core {
+ ( ) => {
+ extern crate std as core;
+ //~^ ERROR macro-expanded `extern crate` items cannot shadow names passed with `--extern`
+ };
+}
+
+fn main() {
+ core::panic!();
+}
+
+define_other_core!();
diff --git a/tests/ui/crashes/ice-6255.stderr b/tests/ui/crashes/ice-6255.stderr
new file mode 100644
index 0000000..d973ea1
--- /dev/null
+++ b/tests/ui/crashes/ice-6255.stderr
@@ -0,0 +1,13 @@
+error: macro-expanded `extern crate` items cannot shadow names passed with `--extern`
+ --> $DIR/ice-6255.rs:6:9
+ |
+LL | extern crate std as core;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | define_other_core!();
+ | --------------------- in this macro invocation
+ |
+ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
diff --git a/tests/ui/crashes/ice-6256.rs b/tests/ui/crashes/ice-6256.rs
new file mode 100644
index 0000000..6f60d45
--- /dev/null
+++ b/tests/ui/crashes/ice-6256.rs
@@ -0,0 +1,13 @@
+// originally from rustc ./src/test/ui/regions/issue-78262.rs
+// ICE: to get the signature of a closure, use substs.as_closure().sig() not fn_sig()
+
+trait TT {}
+
+impl dyn TT {
+ fn func(&self) {}
+}
+
+fn main() {
+ let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types
+ //[nll]~^ ERROR: borrowed data escapes outside of closure
+}
diff --git a/tests/ui/crashes/ice-6256.stderr b/tests/ui/crashes/ice-6256.stderr
new file mode 100644
index 0000000..0e8353a
--- /dev/null
+++ b/tests/ui/crashes/ice-6256.stderr
@@ -0,0 +1,18 @@
+error[E0308]: mismatched types
+ --> $DIR/ice-6256.rs:11:28
+ |
+LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types
+ | ^^^^ lifetime mismatch
+ |
+ = note: expected reference `&(dyn TT + 'static)`
+ found reference `&dyn TT`
+note: the anonymous lifetime #1 defined on the body at 11:13...
+ --> $DIR/ice-6256.rs:11:13
+ |
+LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types
+ | ^^^^^^^^^^^^^^^^^^^^^
+ = note: ...does not necessarily outlive the static lifetime
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/cstring.rs b/tests/ui/cstring.rs
deleted file mode 100644
index 6cdd6b4..0000000
--- a/tests/ui/cstring.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-#![deny(clippy::temporary_cstring_as_ptr)]
-
-fn main() {}
-
-fn temporary_cstring() {
- use std::ffi::CString;
-
- CString::new("foo").unwrap().as_ptr();
- CString::new("foo").expect("dummy").as_ptr();
-}
-
-mod issue4375 {
- use std::ffi::CString;
- use std::os::raw::c_char;
-
- extern "C" {
- fn foo(data: *const c_char);
- }
-
- pub fn bar(v: &[u8]) {
- let cstr = CString::new(v);
- unsafe { foo(cstr.unwrap().as_ptr()) }
- }
-}
diff --git a/tests/ui/cstring.stderr b/tests/ui/cstring.stderr
deleted file mode 100644
index 87cb29b..0000000
--- a/tests/ui/cstring.stderr
+++ /dev/null
@@ -1,46 +0,0 @@
-error: you are getting the inner pointer of a temporary `CString`
- --> $DIR/cstring.rs:8:5
- |
-LL | CString::new("foo").unwrap().as_ptr();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: the lint level is defined here
- --> $DIR/cstring.rs:1:9
- |
-LL | #![deny(clippy::temporary_cstring_as_ptr)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: that pointer will be invalid outside this expression
-help: assign the `CString` to a variable to extend its lifetime
- --> $DIR/cstring.rs:8:5
- |
-LL | CString::new("foo").unwrap().as_ptr();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: you are getting the inner pointer of a temporary `CString`
- --> $DIR/cstring.rs:9:5
- |
-LL | CString::new("foo").expect("dummy").as_ptr();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: that pointer will be invalid outside this expression
-help: assign the `CString` to a variable to extend its lifetime
- --> $DIR/cstring.rs:9:5
- |
-LL | CString::new("foo").expect("dummy").as_ptr();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: you are getting the inner pointer of a temporary `CString`
- --> $DIR/cstring.rs:22:22
- |
-LL | unsafe { foo(cstr.unwrap().as_ptr()) }
- | ^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: that pointer will be invalid outside this expression
-help: assign the `CString` to a variable to extend its lifetime
- --> $DIR/cstring.rs:22:22
- |
-LL | unsafe { foo(cstr.unwrap().as_ptr()) }
- | ^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
-
diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs
index 9e32fe3..5675559 100644
--- a/tests/ui/deprecated.rs
+++ b/tests/ui/deprecated.rs
@@ -9,5 +9,6 @@
#[warn(clippy::unused_label)]
#[warn(clippy::regex_macro)]
#[warn(clippy::drop_bounds)]
+#[warn(clippy::temporary_cstring_as_ptr)]
fn main() {}
diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr
index d3400a7..37b726f 100644
--- a/tests/ui/deprecated.stderr
+++ b/tests/ui/deprecated.stderr
@@ -66,11 +66,17 @@
LL | #[warn(clippy::drop_bounds)]
| ^^^^^^^^^^^^^^^^^^^
+error: lint `clippy::temporary_cstring_as_ptr` has been removed: `this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr``
+ --> $DIR/deprecated.rs:12:8
+ |
+LL | #[warn(clippy::temporary_cstring_as_ptr)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
--> $DIR/deprecated.rs:1:8
|
LL | #[warn(clippy::str_to_string)]
| ^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 12 previous errors
+error: aborting due to 13 previous errors
diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs
new file mode 100644
index 0000000..79a30c2
--- /dev/null
+++ b/tests/ui/field_reassign_with_default.rs
@@ -0,0 +1,110 @@
+#![warn(clippy::field_reassign_with_default)]
+
+#[derive(Default)]
+struct A {
+ i: i32,
+ j: i64,
+}
+
+struct B {
+ i: i32,
+ j: i64,
+}
+
+/// Implements .next() that returns a different number each time.
+struct SideEffect(i32);
+
+impl SideEffect {
+ fn new() -> SideEffect {
+ SideEffect(0)
+ }
+ fn next(&mut self) -> i32 {
+ self.0 += 1;
+ self.0
+ }
+}
+
+fn main() {
+ // wrong, produces first error in stderr
+ let mut a: A = Default::default();
+ a.i = 42;
+
+ // right
+ let mut a: A = Default::default();
+
+ // right
+ let a = A {
+ i: 42,
+ ..Default::default()
+ };
+
+ // right
+ let mut a: A = Default::default();
+ if a.i == 0 {
+ a.j = 12;
+ }
+
+ // right
+ let mut a: A = Default::default();
+ let b = 5;
+
+ // right
+ let mut b = 32;
+ let mut a: A = Default::default();
+ b = 2;
+
+ // right
+ let b: B = B { i: 42, j: 24 };
+
+ // right
+ let mut b: B = B { i: 42, j: 24 };
+ b.i = 52;
+
+ // right
+ let mut b = B { i: 15, j: 16 };
+ let mut a: A = Default::default();
+ b.i = 2;
+
+ // wrong, produces second error in stderr
+ let mut a: A = Default::default();
+ a.j = 43;
+ a.i = 42;
+
+ // wrong, produces third error in stderr
+ let mut a: A = Default::default();
+ a.i = 42;
+ a.j = 43;
+ a.j = 44;
+
+ // wrong, produces fourth error in stderr
+ let mut a = A::default();
+ a.i = 42;
+
+ // wrong, but does not produce an error in stderr, because we can't produce a correct kind of
+ // suggestion with current implementation
+ let mut c: (i32, i32) = Default::default();
+ c.0 = 42;
+ c.1 = 21;
+
+ // wrong, produces the fifth error in stderr
+ let mut a: A = Default::default();
+ a.i = Default::default();
+
+ // wrong, produces the sixth error in stderr
+ let mut a: A = Default::default();
+ a.i = Default::default();
+ a.j = 45;
+
+ // right, because an assignment refers to another field
+ let mut x = A::default();
+ x.i = 42;
+ x.j = 21 + x.i as i64;
+
+ // right, we bail out if there's a reassignment to the same variable, since there is a risk of
+ // side-effects affecting the outcome
+ let mut x = A::default();
+ let mut side_effect = SideEffect::new();
+ x.i = side_effect.next();
+ x.j = 2;
+ x.i = side_effect.next();
+}
diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr
new file mode 100644
index 0000000..c788eba
--- /dev/null
+++ b/tests/ui/field_reassign_with_default.stderr
@@ -0,0 +1,75 @@
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:30:5
+ |
+LL | a.i = 42;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::field-reassign-with-default` implied by `-D warnings`
+note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:29:5
+ |
+LL | let mut a: A = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:70:5
+ |
+LL | a.j = 43;
+ | ^^^^^^^^^
+ |
+note: consider initializing the variable with `A { j: 43, i: 42 }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:69:5
+ |
+LL | let mut a: A = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:75:5
+ |
+LL | a.i = 42;
+ | ^^^^^^^^^
+ |
+note: consider initializing the variable with `A { i: 42, j: 44 }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:74:5
+ |
+LL | let mut a: A = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:81:5
+ |
+LL | a.i = 42;
+ | ^^^^^^^^^
+ |
+note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:80:5
+ |
+LL | let mut a = A::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:91:5
+ |
+LL | a.i = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: consider initializing the variable with `A::default()` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:90:5
+ |
+LL | let mut a: A = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:95:5
+ |
+LL | a.i = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: consider initializing the variable with `A { j: 45, ..Default::default() }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:94:5
+ |
+LL | let mut a: A = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/filter_map_next.rs b/tests/ui/filter_map_next.rs
index f5d051b..dbeb235 100644
--- a/tests/ui/filter_map_next.rs
+++ b/tests/ui/filter_map_next.rs
@@ -3,9 +3,6 @@
fn main() {
let a = ["1", "lol", "3", "NaN", "5"];
- let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
- assert_eq!(element, Some(1));
-
#[rustfmt::skip]
let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
.into_iter()
diff --git a/tests/ui/filter_map_next.stderr b/tests/ui/filter_map_next.stderr
index d69ae21..4542768 100644
--- a/tests/ui/filter_map_next.stderr
+++ b/tests/ui/filter_map_next.stderr
@@ -1,14 +1,5 @@
-error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead.
- --> $DIR/filter_map_next.rs:6:32
- |
-LL | let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: `-D clippy::filter-map-next` implied by `-D warnings`
- = note: replace `filter_map(|s| s.parse().ok()).next()` with `find_map(|s| s.parse().ok())`
-
-error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead.
- --> $DIR/filter_map_next.rs:10:26
+error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead.
+ --> $DIR/filter_map_next.rs:7:26
|
LL | let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
| __________________________^
@@ -19,6 +10,8 @@
LL | | })
LL | | .next();
| |_______________^
+ |
+ = note: `-D clippy::filter-map-next` implied by `-D warnings`
-error: aborting due to 2 previous errors
+error: aborting due to previous error
diff --git a/tests/ui/filter_map_next_fixable.fixed b/tests/ui/filter_map_next_fixable.fixed
new file mode 100644
index 0000000..c3992d7
--- /dev/null
+++ b/tests/ui/filter_map_next_fixable.fixed
@@ -0,0 +1,10 @@
+// run-rustfix
+
+#![warn(clippy::all, clippy::pedantic)]
+
+fn main() {
+ let a = ["1", "lol", "3", "NaN", "5"];
+
+ let element: Option<i32> = a.iter().find_map(|s| s.parse().ok());
+ assert_eq!(element, Some(1));
+}
diff --git a/tests/ui/filter_map_next_fixable.rs b/tests/ui/filter_map_next_fixable.rs
new file mode 100644
index 0000000..447219a
--- /dev/null
+++ b/tests/ui/filter_map_next_fixable.rs
@@ -0,0 +1,10 @@
+// run-rustfix
+
+#![warn(clippy::all, clippy::pedantic)]
+
+fn main() {
+ let a = ["1", "lol", "3", "NaN", "5"];
+
+ let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
+ assert_eq!(element, Some(1));
+}
diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr
new file mode 100644
index 0000000..6c2530e
--- /dev/null
+++ b/tests/ui/filter_map_next_fixable.stderr
@@ -0,0 +1,10 @@
+error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead.
+ --> $DIR/filter_map_next_fixable.rs:8:32
+ |
+LL | let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())`
+ |
+ = note: `-D clippy::filter-map-next` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr
index 84a957a..91718dd 100644
--- a/tests/ui/filter_methods.stderr
+++ b/tests/ui/filter_methods.stderr
@@ -1,4 +1,4 @@
-error: called `filter(p).map(q)` on an `Iterator`
+error: called `filter(..).map(..)` on an `Iterator`
--> $DIR/filter_methods.rs:5:21
|
LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect();
@@ -7,7 +7,7 @@
= note: `-D clippy::filter-map` implied by `-D warnings`
= help: this is more succinctly expressed by calling `.filter_map(..)` instead
-error: called `filter(p).flat_map(q)` on an `Iterator`
+error: called `filter(..).flat_map(..)` on an `Iterator`
--> $DIR/filter_methods.rs:7:21
|
LL | let _: Vec<_> = vec![5_i8; 6]
@@ -19,7 +19,7 @@
|
= help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
-error: called `filter_map(p).flat_map(q)` on an `Iterator`
+error: called `filter_map(..).flat_map(..)` on an `Iterator`
--> $DIR/filter_methods.rs:13:21
|
LL | let _: Vec<_> = vec![5_i8; 6]
@@ -31,7 +31,7 @@
|
= help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
-error: called `filter_map(p).map(q)` on an `Iterator`
+error: called `filter_map(..).map(..)` on an `Iterator`
--> $DIR/filter_methods.rs:19:21
|
LL | let _: Vec<_> = vec![5_i8; 6]
diff --git a/tests/ui/find_map.stderr b/tests/ui/find_map.stderr
index f279850..aea3cc6 100644
--- a/tests/ui/find_map.stderr
+++ b/tests/ui/find_map.stderr
@@ -1,4 +1,4 @@
-error: called `find(p).map(q)` on an `Iterator`
+error: called `find(..).map(..)` on an `Iterator`
--> $DIR/find_map.rs:20:26
|
LL | let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap());
@@ -7,7 +7,7 @@
= note: `-D clippy::find-map` implied by `-D warnings`
= help: this is more succinctly expressed by calling `.find_map(..)` instead
-error: called `find(p).map(q)` on an `Iterator`
+error: called `find(..).map(..)` on an `Iterator`
--> $DIR/find_map.rs:23:29
|
LL | let _: Option<Flavor> = desserts_of_the_week
diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs
new file mode 100644
index 0000000..045eb31
--- /dev/null
+++ b/tests/ui/from_iter_instead_of_collect.rs
@@ -0,0 +1,13 @@
+#![warn(clippy::from_iter_instead_of_collect)]
+
+use std::collections::HashMap;
+use std::iter::FromIterator;
+
+fn main() {
+ let iter_expr = std::iter::repeat(5).take(5);
+ Vec::from_iter(iter_expr);
+
+ HashMap::<usize, &i8>::from_iter(vec![5, 5, 5, 5].iter().enumerate());
+
+ Vec::from_iter(vec![42u32]);
+}
diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr
new file mode 100644
index 0000000..46bdc2f
--- /dev/null
+++ b/tests/ui/from_iter_instead_of_collect.stderr
@@ -0,0 +1,16 @@
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:8:5
+ |
+LL | Vec::from_iter(iter_expr);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect()`
+ |
+ = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings`
+
+error: usage of `FromIterator::from_iter`
+ --> $DIR/from_iter_instead_of_collect.rs:10:5
+ |
+LL | HashMap::<usize, &i8>::from_iter(vec![5, 5, 5, 5].iter().enumerate());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect()`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/get_unwrap.fixed b/tests/ui/get_unwrap.fixed
index 97e6b20..924c02a 100644
--- a/tests/ui/get_unwrap.fixed
+++ b/tests/ui/get_unwrap.fixed
@@ -1,5 +1,5 @@
// run-rustfix
-#![allow(unused_mut)]
+#![allow(unused_mut, clippy::from_iter_instead_of_collect)]
#![deny(clippy::get_unwrap)]
use std::collections::BTreeMap;
diff --git a/tests/ui/get_unwrap.rs b/tests/ui/get_unwrap.rs
index 1c9a71c..c0c37bb 100644
--- a/tests/ui/get_unwrap.rs
+++ b/tests/ui/get_unwrap.rs
@@ -1,5 +1,5 @@
// run-rustfix
-#![allow(unused_mut)]
+#![allow(unused_mut, clippy::from_iter_instead_of_collect)]
#![deny(clippy::get_unwrap)]
use std::collections::BTreeMap;
diff --git a/tests/ui/integer_arithmetic.rs b/tests/ui/integer_arithmetic.rs
index 7b1b64f..b74c93d 100644
--- a/tests/ui/integer_arithmetic.rs
+++ b/tests/ui/integer_arithmetic.rs
@@ -11,6 +11,8 @@
#[rustfmt::skip]
fn main() {
let mut i = 1i32;
+ let mut var1 = 0i32;
+ let mut var2 = -1i32;
1 + i;
i * 2;
1 %
@@ -32,7 +34,15 @@
i -= 1;
i *= 2;
i /= 2;
+ i /= 0;
+ i /= -1;
+ i /= var1;
+ i /= var2;
i %= 2;
+ i %= 0;
+ i %= -1;
+ i %= var1;
+ i %= var2;
i <<= 3;
i >>= 2;
diff --git a/tests/ui/integer_arithmetic.stderr b/tests/ui/integer_arithmetic.stderr
index 83e8a9c..add3b6b 100644
--- a/tests/ui/integer_arithmetic.stderr
+++ b/tests/ui/integer_arithmetic.stderr
@@ -1,5 +1,19 @@
+error: this operation will panic at runtime
+ --> $DIR/integer_arithmetic.rs:37:5
+ |
+LL | i /= 0;
+ | ^^^^^^ attempt to divide `_` by zero
+ |
+ = note: `#[deny(unconditional_panic)]` on by default
+
+error: this operation will panic at runtime
+ --> $DIR/integer_arithmetic.rs:42:5
+ |
+LL | i %= 0;
+ | ^^^^^^ attempt to calculate the remainder of `_` with a divisor of zero
+
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:14:5
+ --> $DIR/integer_arithmetic.rs:16:5
|
LL | 1 + i;
| ^^^^^
@@ -7,125 +21,149 @@
= note: `-D clippy::integer-arithmetic` implied by `-D warnings`
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:15:5
+ --> $DIR/integer_arithmetic.rs:17:5
|
LL | i * 2;
| ^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:16:5
+ --> $DIR/integer_arithmetic.rs:18:5
|
LL | / 1 %
LL | | i / 2; // no error, this is part of the expression in the preceding line
- | |_________^
+ | |_____^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:18:5
+ --> $DIR/integer_arithmetic.rs:20:5
|
LL | i - 2 + 2 - i;
| ^^^^^^^^^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:19:5
+ --> $DIR/integer_arithmetic.rs:21:5
|
LL | -i;
| ^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:20:5
+ --> $DIR/integer_arithmetic.rs:22:5
|
LL | i >> 1;
| ^^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:21:5
+ --> $DIR/integer_arithmetic.rs:23:5
|
LL | i << 1;
| ^^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:31:5
+ --> $DIR/integer_arithmetic.rs:33:5
|
LL | i += 1;
| ^^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:32:5
+ --> $DIR/integer_arithmetic.rs:34:5
|
LL | i -= 1;
| ^^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:33:5
+ --> $DIR/integer_arithmetic.rs:35:5
|
LL | i *= 2;
| ^^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:34:5
+ --> $DIR/integer_arithmetic.rs:38:11
|
-LL | i /= 2;
- | ^^^^^^
+LL | i /= -1;
+ | ^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:35:5
+ --> $DIR/integer_arithmetic.rs:39:5
|
-LL | i %= 2;
- | ^^^^^^
+LL | i /= var1;
+ | ^^^^^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:36:5
+ --> $DIR/integer_arithmetic.rs:40:5
+ |
+LL | i /= var2;
+ | ^^^^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:43:11
+ |
+LL | i %= -1;
+ | ^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:44:5
+ |
+LL | i %= var1;
+ | ^^^^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:45:5
+ |
+LL | i %= var2;
+ | ^^^^^^^^^
+
+error: integer arithmetic detected
+ --> $DIR/integer_arithmetic.rs:46:5
|
LL | i <<= 3;
| ^^^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:37:5
+ --> $DIR/integer_arithmetic.rs:47:5
|
LL | i >>= 2;
| ^^^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:79:5
+ --> $DIR/integer_arithmetic.rs:89:5
|
LL | 3 + &1;
| ^^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:80:5
+ --> $DIR/integer_arithmetic.rs:90:5
|
LL | &3 + 1;
| ^^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:81:5
+ --> $DIR/integer_arithmetic.rs:91:5
|
LL | &3 + &1;
| ^^^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:86:5
+ --> $DIR/integer_arithmetic.rs:96:5
|
LL | a + x
| ^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:90:5
+ --> $DIR/integer_arithmetic.rs:100:5
|
LL | x + y
| ^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:94:5
+ --> $DIR/integer_arithmetic.rs:104:5
|
LL | x + y
| ^^^^^
error: integer arithmetic detected
- --> $DIR/integer_arithmetic.rs:98:5
+ --> $DIR/integer_arithmetic.rs:108:5
|
LL | (&x + &y)
| ^^^^^^^^^
-error: aborting due to 21 previous errors
+error: aborting due to 27 previous errors
diff --git a/tests/ui/invalid_paths.rs b/tests/ui/invalid_paths.rs
new file mode 100644
index 0000000..01e28ae
--- /dev/null
+++ b/tests/ui/invalid_paths.rs
@@ -0,0 +1,23 @@
+#![warn(clippy::internal)]
+
+mod paths {
+ // Good path
+ pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
+
+ // Path to method on inherent impl of a primitive type
+ pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
+
+ // Path to method on inherent impl
+ pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
+
+ // Path with empty segment
+ pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
+
+ // Path with bad crate
+ pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
+
+ // Path with bad module
+ pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
+}
+
+fn main() {}
diff --git a/tests/ui/invalid_paths.stderr b/tests/ui/invalid_paths.stderr
new file mode 100644
index 0000000..bd69d66
--- /dev/null
+++ b/tests/ui/invalid_paths.stderr
@@ -0,0 +1,16 @@
+error: invalid path
+ --> $DIR/invalid_paths.rs:17:5
+ |
+LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::clippy-lints-internal` implied by `-D warnings`
+
+error: invalid path
+ --> $DIR/invalid_paths.rs:20:5
+ |
+LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/item_after_statement.rs b/tests/ui/item_after_statement.rs
index c17a7cb..377e58e 100644
--- a/tests/ui/item_after_statement.rs
+++ b/tests/ui/item_after_statement.rs
@@ -28,7 +28,10 @@
// do not lint this, because it needs to be after `a`
macro_rules! b {
() => {{
- a = 6
+ a = 6;
+ fn say_something() {
+ println!("something");
+ }
}};
}
b!();
diff --git a/tests/ui/item_after_statement.stderr b/tests/ui/item_after_statement.stderr
index f8f010b..68a3c81 100644
--- a/tests/ui/item_after_statement.stderr
+++ b/tests/ui/item_after_statement.stderr
@@ -16,5 +16,18 @@
LL | | }
| |_____^
-error: aborting due to 2 previous errors
+error: adding items after statements is confusing, since items exist from the start of the scope
+ --> $DIR/item_after_statement.rs:32:13
+ |
+LL | / fn say_something() {
+LL | | println!("something");
+LL | | }
+ | |_____________^
+...
+LL | b!();
+ | ----- in this macro invocation
+ |
+ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
diff --git a/tests/ui/iter_skip_next.stderr b/tests/ui/iter_skip_next.stderr
index feedc2f..486de71 100644
--- a/tests/ui/iter_skip_next.stderr
+++ b/tests/ui/iter_skip_next.stderr
@@ -1,4 +1,4 @@
-error: called `skip(x).next()` on an iterator
+error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next.rs:15:28
|
LL | let _ = some_vec.iter().skip(42).next();
@@ -6,19 +6,19 @@
|
= note: `-D clippy::iter-skip-next` implied by `-D warnings`
-error: called `skip(x).next()` on an iterator
+error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next.rs:16:36
|
LL | let _ = some_vec.iter().cycle().skip(42).next();
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
-error: called `skip(x).next()` on an iterator
+error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next.rs:17:20
|
LL | let _ = (1..10).skip(10).next();
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)`
-error: called `skip(x).next()` on an iterator
+error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next.rs:18:33
|
LL | let _ = &some_vec[..].iter().skip(3).next();
diff --git a/tests/ui/large_digit_groups.fixed b/tests/ui/large_digit_groups.fixed
index 859fad2..3430c13 100644
--- a/tests/ui/large_digit_groups.fixed
+++ b/tests/ui/large_digit_groups.fixed
@@ -11,7 +11,7 @@
let _good = (
0b1011_i64,
0o1_234_u32,
- 0x1_234_567,
+ 0x0123_4567,
1_2345_6789,
1234_f32,
1_234.12_f32,
diff --git a/tests/ui/large_digit_groups.stderr b/tests/ui/large_digit_groups.stderr
index b6d9672..13d108b 100644
--- a/tests/ui/large_digit_groups.stderr
+++ b/tests/ui/large_digit_groups.stderr
@@ -1,24 +1,30 @@
-error: digit groups should be smaller
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/large_digit_groups.rs:14:9
+ |
+LL | 0x1_234_567,
+ | ^^^^^^^^^^^ help: consider: `0x0123_4567`
+ |
+ = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
+
+error: digits of hex or binary literal not grouped by four
--> $DIR/large_digit_groups.rs:22:9
|
LL | 0b1_10110_i64,
| ^^^^^^^^^^^^^ help: consider: `0b11_0110_i64`
- |
- = note: `-D clippy::large-digit-groups` implied by `-D warnings`
-error: digits grouped inconsistently by underscores
+error: digits of hex or binary literal not grouped by four
--> $DIR/large_digit_groups.rs:23:9
|
LL | 0xd_e_adbee_f_usize,
| ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize`
- |
- = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
error: digit groups should be smaller
--> $DIR/large_digit_groups.rs:24:9
|
LL | 1_23456_f32,
| ^^^^^^^^^^^ help: consider: `123_456_f32`
+ |
+ = note: `-D clippy::large-digit-groups` implied by `-D warnings`
error: digit groups should be smaller
--> $DIR/large_digit_groups.rs:25:9
@@ -38,5 +44,5 @@
LL | 1_23456.12345_6_f64,
| ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64`
-error: aborting due to 6 previous errors
+error: aborting due to 7 previous errors
diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs
index c299b16..a72a74b 100644
--- a/tests/ui/literals.rs
+++ b/tests/ui/literals.rs
@@ -33,4 +33,9 @@
let fail19 = 12_3456_21;
let fail22 = 3__4___23;
let fail23 = 3__16___23;
+
+ let fail24 = 0xAB_ABC_AB;
+ let fail25 = 0b01_100_101;
+ let ok26 = 0x6_A0_BF;
+ let ok27 = 0b1_0010_0101;
}
diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr
index 0b3af2d..64ceeb3 100644
--- a/tests/ui/literals.stderr
+++ b/tests/ui/literals.stderr
@@ -69,5 +69,19 @@
LL | let fail23 = 3__16___23;
| ^^^^^^^^^^ help: consider: `31_623`
-error: aborting due to 8 previous errors
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/literals.rs:37:18
+ |
+LL | let fail24 = 0xAB_ABC_AB;
+ | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB`
+ |
+ = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
+
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/literals.rs:38:18
+ |
+LL | let fail25 = 0b01_100_101;
+ | ^^^^^^^^^^^^ help: consider: `0b0110_0101`
+
+error: aborting due to 10 previous errors
diff --git a/tests/ui/manual_ok_or.fixed b/tests/ui/manual_ok_or.fixed
new file mode 100644
index 0000000..b42e94b
--- /dev/null
+++ b/tests/ui/manual_ok_or.fixed
@@ -0,0 +1,40 @@
+// run-rustfix
+#![warn(clippy::manual_ok_or)]
+#![allow(clippy::blacklisted_name)]
+#![allow(clippy::redundant_closure)]
+#![allow(dead_code)]
+#![allow(unused_must_use)]
+
+fn main() {
+ // basic case
+ let foo: Option<i32> = None;
+ foo.ok_or("error");
+
+ // eta expansion case
+ foo.ok_or("error");
+
+ // turbo fish syntax
+ None::<i32>.ok_or("error");
+
+ // multiline case
+ #[rustfmt::skip]
+ foo.ok_or(&format!(
+ "{}{}{}{}{}{}{}",
+ "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer"));
+
+ // not applicable, closure isn't direct `Ok` wrapping
+ foo.map_or(Err("error"), |v| Ok(v + 1));
+
+ // not applicable, or side isn't `Result::Err`
+ foo.map_or(Ok::<i32, &str>(1), |v| Ok(v));
+
+ // not applicatble, expr is not a `Result` value
+ foo.map_or(42, |v| v);
+
+ // TODO patterns not covered yet
+ match foo {
+ Some(v) => Ok(v),
+ None => Err("error"),
+ };
+ foo.map_or_else(|| Err("error"), |v| Ok(v));
+}
diff --git a/tests/ui/manual_ok_or.rs b/tests/ui/manual_ok_or.rs
new file mode 100644
index 0000000..e5a6056
--- /dev/null
+++ b/tests/ui/manual_ok_or.rs
@@ -0,0 +1,44 @@
+// run-rustfix
+#![warn(clippy::manual_ok_or)]
+#![allow(clippy::blacklisted_name)]
+#![allow(clippy::redundant_closure)]
+#![allow(dead_code)]
+#![allow(unused_must_use)]
+
+fn main() {
+ // basic case
+ let foo: Option<i32> = None;
+ foo.map_or(Err("error"), |v| Ok(v));
+
+ // eta expansion case
+ foo.map_or(Err("error"), Ok);
+
+ // turbo fish syntax
+ None::<i32>.map_or(Err("error"), |v| Ok(v));
+
+ // multiline case
+ #[rustfmt::skip]
+ foo.map_or(Err::<i32, &str>(
+ &format!(
+ "{}{}{}{}{}{}{}",
+ "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer")
+ ),
+ |v| Ok(v),
+ );
+
+ // not applicable, closure isn't direct `Ok` wrapping
+ foo.map_or(Err("error"), |v| Ok(v + 1));
+
+ // not applicable, or side isn't `Result::Err`
+ foo.map_or(Ok::<i32, &str>(1), |v| Ok(v));
+
+ // not applicatble, expr is not a `Result` value
+ foo.map_or(42, |v| v);
+
+ // TODO patterns not covered yet
+ match foo {
+ Some(v) => Ok(v),
+ None => Err("error"),
+ };
+ foo.map_or_else(|| Err("error"), |v| Ok(v));
+}
diff --git a/tests/ui/manual_ok_or.stderr b/tests/ui/manual_ok_or.stderr
new file mode 100644
index 0000000..8ea10ac
--- /dev/null
+++ b/tests/ui/manual_ok_or.stderr
@@ -0,0 +1,41 @@
+error: this pattern reimplements `Option::ok_or`
+ --> $DIR/manual_ok_or.rs:11:5
+ |
+LL | foo.map_or(Err("error"), |v| Ok(v));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")`
+ |
+ = note: `-D clippy::manual-ok-or` implied by `-D warnings`
+
+error: this pattern reimplements `Option::ok_or`
+ --> $DIR/manual_ok_or.rs:14:5
+ |
+LL | foo.map_or(Err("error"), Ok);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")`
+
+error: this pattern reimplements `Option::ok_or`
+ --> $DIR/manual_ok_or.rs:17:5
+ |
+LL | None::<i32>.map_or(Err("error"), |v| Ok(v));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `None::<i32>.ok_or("error")`
+
+error: this pattern reimplements `Option::ok_or`
+ --> $DIR/manual_ok_or.rs:21:5
+ |
+LL | / foo.map_or(Err::<i32, &str>(
+LL | | &format!(
+LL | | "{}{}{}{}{}{}{}",
+LL | | "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer")
+LL | | ),
+LL | | |v| Ok(v),
+LL | | );
+ | |_____^
+ |
+help: replace with
+ |
+LL | foo.ok_or(&format!(
+LL | "{}{}{}{}{}{}{}",
+LL | "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer"));
+ |
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/map_collect_result_unit.fixed b/tests/ui/map_collect_result_unit.fixed
new file mode 100644
index 0000000..e66c9cc
--- /dev/null
+++ b/tests/ui/map_collect_result_unit.fixed
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::map_collect_result_unit)]
+
+fn main() {
+ {
+ let _ = (0..3).try_for_each(|t| Err(t + 1));
+ let _: Result<(), _> = (0..3).try_for_each(|t| Err(t + 1));
+
+ let _ = (0..3).try_for_each(|t| Err(t + 1));
+ }
+}
+
+fn _ignore() {
+ let _ = (0..3).map(|t| Err(t + 1)).collect::<Result<Vec<i32>, _>>();
+ let _ = (0..3).map(|t| Err(t + 1)).collect::<Vec<Result<(), _>>>();
+}
diff --git a/tests/ui/map_collect_result_unit.rs b/tests/ui/map_collect_result_unit.rs
new file mode 100644
index 0000000..6f08f4c
--- /dev/null
+++ b/tests/ui/map_collect_result_unit.rs
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::map_collect_result_unit)]
+
+fn main() {
+ {
+ let _ = (0..3).map(|t| Err(t + 1)).collect::<Result<(), _>>();
+ let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect();
+
+ let _ = (0..3).try_for_each(|t| Err(t + 1));
+ }
+}
+
+fn _ignore() {
+ let _ = (0..3).map(|t| Err(t + 1)).collect::<Result<Vec<i32>, _>>();
+ let _ = (0..3).map(|t| Err(t + 1)).collect::<Vec<Result<(), _>>>();
+}
diff --git a/tests/ui/map_collect_result_unit.stderr b/tests/ui/map_collect_result_unit.stderr
new file mode 100644
index 0000000..8b06e13
--- /dev/null
+++ b/tests/ui/map_collect_result_unit.stderr
@@ -0,0 +1,16 @@
+error: `.map().collect()` can be replaced with `.try_for_each()`
+ --> $DIR/map_collect_result_unit.rs:6:17
+ |
+LL | let _ = (0..3).map(|t| Err(t + 1)).collect::<Result<(), _>>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))`
+ |
+ = note: `-D clippy::map-collect-result-unit` implied by `-D warnings`
+
+error: `.map().collect()` can be replaced with `.try_for_each()`
+ --> $DIR/map_collect_result_unit.rs:7:32
+ |
+LL | let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs
index 5859440..87e16f5 100644
--- a/tests/ui/map_unwrap_or.rs
+++ b/tests/ui/map_unwrap_or.rs
@@ -1,4 +1,3 @@
-// FIXME: Add "run-rustfix" once it's supported for multipart suggestions
// aux-build:option_helpers.rs
#![warn(clippy::map_unwrap_or)]
@@ -47,10 +46,6 @@
let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
// Check for `option.map(_).unwrap_or_else(_)` use.
- // single line case
- let _ = opt.map(|x| x + 1)
- // Should lint even though this call is on a separate line.
- .unwrap_or_else(|| 0);
// Multi-line cases.
let _ = opt.map(|x| {
x + 1
@@ -60,37 +55,24 @@
.unwrap_or_else(||
0
);
- // Macro case.
- // Should not lint.
- let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0);
-
- // Issue #4144
- {
- let mut frequencies = HashMap::new();
- let word = "foo";
-
- frequencies
- .get_mut(word)
- .map(|count| {
- *count += 1;
- })
- .unwrap_or_else(|| {
- frequencies.insert(word.to_owned(), 1);
- });
- }
}
+#[rustfmt::skip]
fn result_methods() {
let res: Result<i32, ()> = Ok(1);
// Check for `result.map(_).unwrap_or_else(_)` use.
- // single line case
- let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line
- // multi line cases
- let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0);
- let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0);
+ // multi line cases
+ let _ = res.map(|x| {
+ x + 1
+ }
+ ).unwrap_or_else(|_e| 0);
+ let _ = res.map(|x| x + 1)
+ .unwrap_or_else(|_e| {
+ 0
+ });
// macro case
- let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint
+ let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint
}
fn main() {
diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr
index b62080a..96b9d6c 100644
--- a/tests/ui/map_unwrap_or.stderr
+++ b/tests/ui/map_unwrap_or.stderr
@@ -1,5 +1,5 @@
-error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead
- --> $DIR/map_unwrap_or.rs:17:13
+error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:16:13
|
LL | let _ = opt.map(|x| x + 1)
| _____________^
@@ -8,13 +8,13 @@
| |_____________________^
|
= note: `-D clippy::map-unwrap-or` implied by `-D warnings`
-help: use `map_or(a, f)` instead
+help: use `map_or(<a>, <f>)` instead
|
LL | let _ = opt.map_or(0, |x| x + 1);
| ^^^^^^ ^^ --
-error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead
- --> $DIR/map_unwrap_or.rs:21:13
+error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:20:13
|
LL | let _ = opt.map(|x| {
| _____________^
@@ -23,7 +23,7 @@
LL | | ).unwrap_or(0);
| |__________________^
|
-help: use `map_or(a, f)` instead
+help: use `map_or(<a>, <f>)` instead
|
LL | let _ = opt.map_or(0, |x| {
LL | x + 1
@@ -31,8 +31,8 @@
LL | );
|
-error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead
- --> $DIR/map_unwrap_or.rs:25:13
+error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:24:13
|
LL | let _ = opt.map(|x| x + 1)
| _____________^
@@ -41,26 +41,26 @@
LL | | });
| |__________^
|
-help: use `map_or(a, f)` instead
+help: use `map_or(<a>, <f>)` instead
|
LL | let _ = opt.map_or({
LL | 0
LL | }, |x| x + 1);
|
-error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
- --> $DIR/map_unwrap_or.rs:30:13
+error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
+ --> $DIR/map_unwrap_or.rs:29:13
|
LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: use `and_then(f)` instead
+help: use `and_then(<f>)` instead
|
LL | let _ = opt.and_then(|x| Some(x + 1));
| ^^^^^^^^ --
-error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
- --> $DIR/map_unwrap_or.rs:32:13
+error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
+ --> $DIR/map_unwrap_or.rs:31:13
|
LL | let _ = opt.map(|x| {
| _____________^
@@ -69,7 +69,7 @@
LL | | ).unwrap_or(None);
| |_____________________^
|
-help: use `and_then(f)` instead
+help: use `and_then(<f>)` instead
|
LL | let _ = opt.and_then(|x| {
LL | Some(x + 1)
@@ -77,8 +77,8 @@
LL | );
|
-error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
- --> $DIR/map_unwrap_or.rs:36:13
+error: called `map(<f>).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(<f>)` instead
+ --> $DIR/map_unwrap_or.rs:35:13
|
LL | let _ = opt
| _____________^
@@ -86,35 +86,24 @@
LL | | .unwrap_or(None);
| |________________________^
|
-help: use `and_then(f)` instead
+help: use `and_then(<f>)` instead
|
LL | .and_then(|x| Some(x + 1));
| ^^^^^^^^ --
-error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead
- --> $DIR/map_unwrap_or.rs:47:13
+error: called `map(<f>).unwrap_or(<a>)` on an `Option` value. This can be done more directly by calling `map_or(<a>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:46:13
|
LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: use `map_or(a, f)` instead
+help: use `map_or(<a>, <f>)` instead
|
LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p));
| ^^^^^^ ^^^ --
-error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead
- --> $DIR/map_unwrap_or.rs:51:13
- |
-LL | let _ = opt.map(|x| x + 1)
- | _____________^
-LL | | // Should lint even though this call is on a separate line.
-LL | | .unwrap_or_else(|| 0);
- | |_____________________________^
- |
- = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)`
-
-error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead
- --> $DIR/map_unwrap_or.rs:55:13
+error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:50:13
|
LL | let _ = opt.map(|x| {
| _____________^
@@ -123,8 +112,8 @@
LL | | ).unwrap_or_else(|| 0);
| |__________________________^
-error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead
- --> $DIR/map_unwrap_or.rs:59:13
+error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:54:13
|
LL | let _ = opt.map(|x| x + 1)
| _____________^
@@ -133,29 +122,25 @@
LL | | );
| |_________^
-error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead
- --> $DIR/map_unwrap_or.rs:88:13
+error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:66:13
|
-LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)`
+LL | let _ = res.map(|x| {
+ | _____________^
+LL | | x + 1
+LL | | }
+LL | | ).unwrap_or_else(|_e| 0);
+ | |____________________________^
-error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead
- --> $DIR/map_unwrap_or.rs:90:13
+error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
+ --> $DIR/map_unwrap_or.rs:70:13
|
-LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)`
+LL | let _ = res.map(|x| x + 1)
+ | _____________^
+LL | | .unwrap_or_else(|_e| {
+LL | | 0
+LL | | });
+ | |__________^
-error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead
- --> $DIR/map_unwrap_or.rs:91:13
- |
-LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)`
-
-error: aborting due to 13 previous errors
+error: aborting due to 11 previous errors
diff --git a/tests/ui/map_unwrap_or_fixable.fixed b/tests/ui/map_unwrap_or_fixable.fixed
new file mode 100644
index 0000000..bd5b4f7
--- /dev/null
+++ b/tests/ui/map_unwrap_or_fixable.fixed
@@ -0,0 +1,54 @@
+// run-rustfix
+// aux-build:option_helpers.rs
+
+#![warn(clippy::map_unwrap_or)]
+
+#[macro_use]
+extern crate option_helpers;
+
+use std::collections::HashMap;
+
+#[rustfmt::skip]
+fn option_methods() {
+ let opt = Some(1);
+
+ // Check for `option.map(_).unwrap_or_else(_)` use.
+ // single line case
+ let _ = opt.map_or_else(|| 0, |x| x + 1);
+
+ // Macro case.
+ // Should not lint.
+ let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0);
+
+ // Issue #4144
+ {
+ let mut frequencies = HashMap::new();
+ let word = "foo";
+
+ frequencies
+ .get_mut(word)
+ .map(|count| {
+ *count += 1;
+ })
+ .unwrap_or_else(|| {
+ frequencies.insert(word.to_owned(), 1);
+ });
+ }
+}
+
+#[rustfmt::skip]
+fn result_methods() {
+ let res: Result<i32, ()> = Ok(1);
+
+ // Check for `result.map(_).unwrap_or_else(_)` use.
+ // single line case
+ let _ = res.map_or_else(|_e| 0, |x| x + 1);
+
+ // macro case
+ let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint
+}
+
+fn main() {
+ option_methods();
+ result_methods();
+}
diff --git a/tests/ui/map_unwrap_or_fixable.rs b/tests/ui/map_unwrap_or_fixable.rs
new file mode 100644
index 0000000..0b892ca
--- /dev/null
+++ b/tests/ui/map_unwrap_or_fixable.rs
@@ -0,0 +1,58 @@
+// run-rustfix
+// aux-build:option_helpers.rs
+
+#![warn(clippy::map_unwrap_or)]
+
+#[macro_use]
+extern crate option_helpers;
+
+use std::collections::HashMap;
+
+#[rustfmt::skip]
+fn option_methods() {
+ let opt = Some(1);
+
+ // Check for `option.map(_).unwrap_or_else(_)` use.
+ // single line case
+ let _ = opt.map(|x| x + 1)
+ // Should lint even though this call is on a separate line.
+ .unwrap_or_else(|| 0);
+
+ // Macro case.
+ // Should not lint.
+ let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0);
+
+ // Issue #4144
+ {
+ let mut frequencies = HashMap::new();
+ let word = "foo";
+
+ frequencies
+ .get_mut(word)
+ .map(|count| {
+ *count += 1;
+ })
+ .unwrap_or_else(|| {
+ frequencies.insert(word.to_owned(), 1);
+ });
+ }
+}
+
+#[rustfmt::skip]
+fn result_methods() {
+ let res: Result<i32, ()> = Ok(1);
+
+ // Check for `result.map(_).unwrap_or_else(_)` use.
+ // single line case
+ let _ = res.map(|x| x + 1)
+ // should lint even though this call is on a separate line
+ .unwrap_or_else(|_e| 0);
+
+ // macro case
+ let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint
+}
+
+fn main() {
+ option_methods();
+ result_methods();
+}
diff --git a/tests/ui/map_unwrap_or_fixable.stderr b/tests/ui/map_unwrap_or_fixable.stderr
new file mode 100644
index 0000000..1837bc2
--- /dev/null
+++ b/tests/ui/map_unwrap_or_fixable.stderr
@@ -0,0 +1,22 @@
+error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling `map_or_else(<g>, <f>)` instead
+ --> $DIR/map_unwrap_or_fixable.rs:17:13
+ |
+LL | let _ = opt.map(|x| x + 1)
+ | _____________^
+LL | | // Should lint even though this call is on a separate line.
+LL | | .unwrap_or_else(|| 0);
+ | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)`
+ |
+ = note: `-D clippy::map-unwrap-or` implied by `-D warnings`
+
+error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling `.map_or_else(<g>, <f>)` instead
+ --> $DIR/map_unwrap_or_fixable.rs:47:13
+ |
+LL | let _ = res.map(|x| x + 1)
+ | _____________^
+LL | | // should lint even though this call is on a separate line
+LL | | .unwrap_or_else(|_e| 0);
+ | |_______________________________^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed
index f3e1909..7f4ebf5 100644
--- a/tests/ui/match_expr_like_matches_macro.fixed
+++ b/tests/ui/match_expr_like_matches_macro.fixed
@@ -1,7 +1,7 @@
// run-rustfix
#![warn(clippy::match_like_matches_macro)]
-#![allow(unreachable_patterns)]
+#![allow(unreachable_patterns, dead_code)]
fn main() {
let x = Some(5);
@@ -33,4 +33,70 @@
_ => true,
None => false,
};
+
+ enum E {
+ A(u32),
+ B(i32),
+ C,
+ D,
+ };
+ let x = E::A(2);
+ {
+ // lint
+ let _ans = matches!(x, E::A(_) | E::B(_));
+ }
+ {
+ // lint
+ let _ans = !matches!(x, E::B(_) | E::C);
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => false,
+ E::C => true,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => true,
+ E::B(_) => false,
+ E::C => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) if a < 10 => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) => a == 10,
+ E::B(_) => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => true,
+ _ => false,
+ };
+ }
}
diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs
index fbae7c1..aee56dd 100644
--- a/tests/ui/match_expr_like_matches_macro.rs
+++ b/tests/ui/match_expr_like_matches_macro.rs
@@ -1,7 +1,7 @@
// run-rustfix
#![warn(clippy::match_like_matches_macro)]
-#![allow(unreachable_patterns)]
+#![allow(unreachable_patterns, dead_code)]
fn main() {
let x = Some(5);
@@ -45,4 +45,78 @@
_ => true,
None => false,
};
+
+ enum E {
+ A(u32),
+ B(i32),
+ C,
+ D,
+ };
+ let x = E::A(2);
+ {
+ // lint
+ let _ans = match x {
+ E::A(_) => true,
+ E::B(_) => true,
+ _ => false,
+ };
+ }
+ {
+ // lint
+ let _ans = match x {
+ E::B(_) => false,
+ E::C => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => false,
+ E::C => true,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => true,
+ E::B(_) => false,
+ E::C => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) if a < 10 => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) => a == 10,
+ E::B(_) => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => true,
+ _ => false,
+ };
+ }
}
diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr
index 4668f85..c52e41c 100644
--- a/tests/ui/match_expr_like_matches_macro.stderr
+++ b/tests/ui/match_expr_like_matches_macro.stderr
@@ -48,5 +48,27 @@
LL | let _zzz = if let Some(5) = x { true } else { false };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))`
-error: aborting due to 5 previous errors
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:58:20
+ |
+LL | let _ans = match x {
+ | ____________________^
+LL | | E::A(_) => true,
+LL | | E::B(_) => true,
+LL | | _ => false,
+LL | | };
+ | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:66:20
+ |
+LL | let _ans = match x {
+ | ____________________^
+LL | | E::B(_) => false,
+LL | | E::C => false,
+LL | | _ => true,
+LL | | };
+ | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)`
+
+error: aborting due to 7 previous errors
diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs
index e1401d2..06d9149 100644
--- a/tests/ui/match_same_arms2.rs
+++ b/tests/ui/match_same_arms2.rs
@@ -119,6 +119,22 @@
unreachable!();
},
}
+
+ match_expr_like_matches_macro_priority();
+}
+
+fn match_expr_like_matches_macro_priority() {
+ enum E {
+ A,
+ B,
+ C,
+ }
+ let x = E::A;
+ let _ans = match x {
+ E::A => false,
+ E::B => false,
+ _ => true,
+ };
}
fn main() {}
diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr
index 26c65f3..fccaf80 100644
--- a/tests/ui/match_same_arms2.stderr
+++ b/tests/ui/match_same_arms2.stderr
@@ -141,5 +141,18 @@
| ^^^^^
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 7 previous errors
+error: match expression looks like `matches!` macro
+ --> $DIR/match_same_arms2.rs:133:16
+ |
+LL | let _ans = match x {
+ | ________________^
+LL | | E::A => false,
+LL | | E::B => false,
+LL | | _ => true,
+LL | | };
+ | |_____^ help: try this: `!matches!(x, E::A | E::B)`
+ |
+ = note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
+
+error: aborting due to 8 previous errors
diff --git a/tests/ui/match_type_on_diag_item.stderr b/tests/ui/match_type_on_diag_item.stderr
index 5e5fe9e..82465db 100644
--- a/tests/ui/match_type_on_diag_item.stderr
+++ b/tests/ui/match_type_on_diag_item.stderr
@@ -2,7 +2,7 @@
--> $DIR/match_type_on_diag_item.rs:41:17
|
LL | let _ = match_type(cx, ty, &paths::VEC);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::vec_type)`
|
note: the lint level is defined here
--> $DIR/match_type_on_diag_item.rs:1:9
@@ -15,19 +15,19 @@
--> $DIR/match_type_on_diag_item.rs:42:17
|
LL | let _ = match_type(cx, ty, &OPTION);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(option_type))`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::option_type)`
error: usage of `utils::match_type()` on a type diagnostic item
--> $DIR/match_type_on_diag_item.rs:43:17
|
LL | let _ = match_type(cx, ty, &["core", "result", "Result"]);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(result_type))`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::result_type)`
error: usage of `utils::match_type()` on a type diagnostic item
--> $DIR/match_type_on_diag_item.rs:46:17
|
LL | let _ = utils::match_type(cx, ty, rc_path);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(Rc))`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::Rc)`
error: aborting due to 4 previous errors
diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs
index 80dd2f7..d93e5b1 100644
--- a/tests/ui/methods.rs
+++ b/tests/ui/methods.rs
@@ -122,16 +122,13 @@
fn filter_next() {
let v = vec![3, 2, 1, 0, -1, -2, -3];
- // Single-line case.
- let _ = v.iter().filter(|&x| *x < 0).next();
-
// Multi-line case.
let _ = v.iter().filter(|&x| {
*x < 0
}
).next();
- // Check that hat we don't lint if the caller is not an `Iterator`.
+ // Check that we don't lint if the caller is not an `Iterator`.
let foo = IteratorFalsePositives { foo: 0 };
let _ = foo.filter().next();
}
diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr
index 2a0a43e..8a281c2 100644
--- a/tests/ui/methods.stderr
+++ b/tests/ui/methods.stderr
@@ -8,27 +8,20 @@
|
= note: `-D clippy::new-ret-no-self` implied by `-D warnings`
-error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead.
+error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead.
--> $DIR/methods.rs:126:13
|
-LL | let _ = v.iter().filter(|&x| *x < 0).next();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: `-D clippy::filter-next` implied by `-D warnings`
- = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)`
-
-error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead.
- --> $DIR/methods.rs:129:13
- |
LL | let _ = v.iter().filter(|&x| {
| _____________^
LL | | *x < 0
LL | | }
LL | | ).next();
| |___________________________^
+ |
+ = note: `-D clippy::filter-next` implied by `-D warnings`
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:146:22
+ --> $DIR/methods.rs:143:22
|
LL | let _ = v.iter().find(|&x| *x < 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)`
@@ -36,25 +29,25 @@
= note: `-D clippy::search-is-some` implied by `-D warnings`
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:147:20
+ --> $DIR/methods.rs:144:20
|
LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)`
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:148:20
+ --> $DIR/methods.rs:145:20
|
LL | let _ = (0..1).find(|x| *x == 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)`
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:149:22
+ --> $DIR/methods.rs:146:22
|
LL | let _ = v.iter().find(|x| **x == 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)`
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:152:13
+ --> $DIR/methods.rs:149:13
|
LL | let _ = v.iter().find(|&x| {
| _____________^
@@ -64,13 +57,13 @@
| |______________________________^
error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:158:22
+ --> $DIR/methods.rs:155:22
|
LL | let _ = v.iter().position(|&x| x < 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)`
error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:161:13
+ --> $DIR/methods.rs:158:13
|
LL | let _ = v.iter().position(|&x| {
| _____________^
@@ -80,13 +73,13 @@
| |______________________________^
error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:167:22
+ --> $DIR/methods.rs:164:22
|
LL | let _ = v.iter().rposition(|&x| x < 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)`
error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:170:13
+ --> $DIR/methods.rs:167:13
|
LL | let _ = v.iter().rposition(|&x| {
| _____________^
@@ -95,5 +88,5 @@
LL | | ).is_some();
| |______________________________^
-error: aborting due to 12 previous errors
+error: aborting due to 11 previous errors
diff --git a/tests/ui/methods_fixable.fixed b/tests/ui/methods_fixable.fixed
new file mode 100644
index 0000000..ee7c1b0
--- /dev/null
+++ b/tests/ui/methods_fixable.fixed
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#![warn(clippy::filter_next)]
+
+/// Checks implementation of `FILTER_NEXT` lint.
+fn main() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+
+ // Single-line case.
+ let _ = v.iter().find(|&x| *x < 0);
+}
diff --git a/tests/ui/methods_fixable.rs b/tests/ui/methods_fixable.rs
new file mode 100644
index 0000000..6d0f1b7
--- /dev/null
+++ b/tests/ui/methods_fixable.rs
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#![warn(clippy::filter_next)]
+
+/// Checks implementation of `FILTER_NEXT` lint.
+fn main() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+
+ // Single-line case.
+ let _ = v.iter().filter(|&x| *x < 0).next();
+}
diff --git a/tests/ui/methods_fixable.stderr b/tests/ui/methods_fixable.stderr
new file mode 100644
index 0000000..70e7c3d
--- /dev/null
+++ b/tests/ui/methods_fixable.stderr
@@ -0,0 +1,10 @@
+error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead.
+ --> $DIR/methods_fixable.rs:10:13
+ |
+LL | let _ = v.iter().filter(|&x| *x < 0).next();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `v.iter().find(|&x| *x < 0)`
+ |
+ = note: `-D clippy::filter-next` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs
index 4cf03e8..4f6e535 100644
--- a/tests/ui/needless_collect_indirect.rs
+++ b/tests/ui/needless_collect_indirect.rs
@@ -16,4 +16,10 @@
.into_iter()
.map(|x| (*x, *x + 1))
.collect::<HashMap<_, _>>();
+
+ // #6202
+ let a = "a".to_string();
+ let sample = vec![a.clone(), "b".to_string(), "c".to_string()];
+ let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
+ non_copy_contains.contains(&a);
}
diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr
index 0c1e61d..fb807da 100644
--- a/tests/ui/needless_collect_indirect.stderr
+++ b/tests/ui/needless_collect_indirect.stderr
@@ -48,8 +48,21 @@
help: Check if the original Iterator contains an element instead of collecting then checking
|
LL |
-LL | sample.iter().any(|x| x == &&5);
+LL | sample.iter().any(|x| x == &5);
|
-error: aborting due to 4 previous errors
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:23:5
+ |
+LL | / let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
+LL | | non_copy_contains.contains(&a);
+ | |____^
+ |
+help: Check if the original Iterator contains an element instead of collecting then checking
+ |
+LL |
+LL | sample.into_iter().any(|x| x == a);
+ |
+
+error: aborting due to 5 previous errors
diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed
index a7fb00a..47e7460 100644
--- a/tests/ui/option_if_let_else.fixed
+++ b/tests/ui/option_if_let_else.fixed
@@ -1,6 +1,7 @@
// run-rustfix
#![warn(clippy::option_if_let_else)]
#![allow(clippy::redundant_closure)]
+#![allow(clippy::ref_option_ref)]
fn bad1(string: Option<&str>) -> (bool, &str) {
string.map_or((false, "hello"), |x| (true, x))
diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs
index 895fd86..e2f8dec 100644
--- a/tests/ui/option_if_let_else.rs
+++ b/tests/ui/option_if_let_else.rs
@@ -1,6 +1,7 @@
// run-rustfix
#![warn(clippy::option_if_let_else)]
#![allow(clippy::redundant_closure)]
+#![allow(clippy::ref_option_ref)]
fn bad1(string: Option<&str>) -> (bool, &str) {
if let Some(x) = string {
diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr
index b69fe76..7aab068 100644
--- a/tests/ui/option_if_let_else.stderr
+++ b/tests/ui/option_if_let_else.stderr
@@ -1,5 +1,5 @@
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:6:5
+ --> $DIR/option_if_let_else.rs:7:5
|
LL | / if let Some(x) = string {
LL | | (true, x)
@@ -11,7 +11,7 @@
= note: `-D clippy::option-if-let-else` implied by `-D warnings`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:16:12
+ --> $DIR/option_if_let_else.rs:17:12
|
LL | } else if let Some(x) = string {
| ____________^
@@ -22,19 +22,19 @@
| |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:24:13
+ --> $DIR/option_if_let_else.rs:25:13
|
LL | let _ = if let Some(s) = *string { s.len() } else { 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:25:13
+ --> $DIR/option_if_let_else.rs:26:13
|
LL | let _ = if let Some(s) = &num { s } else { &0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:26:13
+ --> $DIR/option_if_let_else.rs:27:13
|
LL | let _ = if let Some(s) = &mut num {
| _____________^
@@ -54,13 +54,13 @@
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:32:13
+ --> $DIR/option_if_let_else.rs:33:13
|
LL | let _ = if let Some(ref s) = num { s } else { &0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:33:13
+ --> $DIR/option_if_let_else.rs:34:13
|
LL | let _ = if let Some(mut s) = num {
| _____________^
@@ -80,7 +80,7 @@
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:39:13
+ --> $DIR/option_if_let_else.rs:40:13
|
LL | let _ = if let Some(ref mut s) = num {
| _____________^
@@ -100,7 +100,7 @@
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:48:5
+ --> $DIR/option_if_let_else.rs:49:5
|
LL | / if let Some(x) = arg {
LL | | let y = x * x;
@@ -119,7 +119,7 @@
|
error: use Option::map_or_else instead of an if let/else
- --> $DIR/option_if_let_else.rs:61:13
+ --> $DIR/option_if_let_else.rs:62:13
|
LL | let _ = if let Some(x) = arg {
| _____________^
@@ -131,7 +131,7 @@
| |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)`
error: use Option::map_or_else instead of an if let/else
- --> $DIR/option_if_let_else.rs:70:13
+ --> $DIR/option_if_let_else.rs:71:13
|
LL | let _ = if let Some(x) = arg {
| _____________^
@@ -154,7 +154,7 @@
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:99:13
+ --> $DIR/option_if_let_else.rs:100:13
|
LL | let _ = if let Some(x) = optional { x + 2 } else { 5 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
diff --git a/tests/ui/option_map_or_none.stderr b/tests/ui/option_map_or_none.stderr
index 6f70798..1cba294 100644
--- a/tests/ui/option_map_or_none.stderr
+++ b/tests/ui/option_map_or_none.stderr
@@ -1,4 +1,4 @@
-error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
--> $DIR/option_map_or_none.rs:10:13
|
LL | let _ = opt.map_or(None, |x| Some(x + 1));
@@ -6,7 +6,7 @@
|
= note: `-D clippy::option-map-or-none` implied by `-D warnings`
-error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
--> $DIR/option_map_or_none.rs:13:13
|
LL | let _ = opt.map_or(None, |x| {
diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs
new file mode 100644
index 0000000..b2c275d
--- /dev/null
+++ b/tests/ui/ref_option_ref.rs
@@ -0,0 +1,47 @@
+#![allow(unused)]
+#![warn(clippy::ref_option_ref)]
+
+// This lint is not tagged as run-rustfix because automatically
+// changing the type of a variable would also means changing
+// all usages of this variable to match and This is not handled
+// by this lint.
+
+static THRESHOLD: i32 = 10;
+static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD);
+const CONST_THRESHOLD: &i32 = &10;
+const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD);
+
+type RefOptRefU32<'a> = &'a Option<&'a u32>;
+type RefOptRef<'a, T> = &'a Option<&'a T>;
+
+fn foo(data: &Option<&u32>) {}
+
+fn bar(data: &u32) -> &Option<&u32> {
+ &None
+}
+
+struct StructRef<'a> {
+ data: &'a Option<&'a u32>,
+}
+
+struct StructTupleRef<'a>(u32, &'a Option<&'a u32>);
+
+enum EnumRef<'a> {
+ Variant1(u32),
+ Variant2(&'a Option<&'a u32>),
+}
+
+trait RefOptTrait {
+ type A;
+ fn foo(&self, _: Self::A);
+}
+
+impl RefOptTrait for u32 {
+ type A = &'static Option<&'static Self>;
+
+ fn foo(&self, _: Self::A) {}
+}
+
+fn main() {
+ let x: &Option<&u32> = &None;
+}
diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr
new file mode 100644
index 0000000..4e7fc80
--- /dev/null
+++ b/tests/ui/ref_option_ref.stderr
@@ -0,0 +1,70 @@
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:10:23
+ |
+LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD);
+ | ^^^^^^^^^^^^^ help: try: `Option<&i32>`
+ |
+ = note: `-D clippy::ref-option-ref` implied by `-D warnings`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:12:18
+ |
+LL | const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD);
+ | ^^^^^^^^^^^^^ help: try: `Option<&i32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:14:25
+ |
+LL | type RefOptRefU32<'a> = &'a Option<&'a u32>;
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:15:25
+ |
+LL | type RefOptRef<'a, T> = &'a Option<&'a T>;
+ | ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:17:14
+ |
+LL | fn foo(data: &Option<&u32>) {}
+ | ^^^^^^^^^^^^^ help: try: `Option<&u32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:19:23
+ |
+LL | fn bar(data: &u32) -> &Option<&u32> {
+ | ^^^^^^^^^^^^^ help: try: `Option<&u32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:24:11
+ |
+LL | data: &'a Option<&'a u32>,
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:27:32
+ |
+LL | struct StructTupleRef<'a>(u32, &'a Option<&'a u32>);
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:31:14
+ |
+LL | Variant2(&'a Option<&'a u32>),
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:40:14
+ |
+LL | type A = &'static Option<&'static Self>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'static Self>`
+
+error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>`
+ --> $DIR/ref_option_ref.rs:46:12
+ |
+LL | let x: &Option<&u32> = &None;
+ | ^^^^^^^^^^^^^ help: try: `Option<&u32>`
+
+error: aborting due to 11 previous errors
+
diff --git a/tests/ui/single_char_add_str.fixed b/tests/ui/single_char_add_str.fixed
new file mode 100644
index 0000000..63a6d37
--- /dev/null
+++ b/tests/ui/single_char_add_str.fixed
@@ -0,0 +1,45 @@
+// run-rustfix
+#![warn(clippy::single_char_add_str)]
+
+macro_rules! get_string {
+ () => {
+ String::from("Hello world!")
+ };
+}
+
+fn main() {
+ // `push_str` tests
+
+ let mut string = String::new();
+ string.push('R');
+ string.push('\'');
+
+ string.push('u');
+ string.push_str("st");
+ string.push_str("");
+ string.push('\x52');
+ string.push('\u{0052}');
+ string.push('a');
+
+ get_string!().push('ö');
+
+ // `insert_str` tests
+
+ let mut string = String::new();
+ string.insert(0, 'R');
+ string.insert(1, '\'');
+
+ string.insert(0, 'u');
+ string.insert_str(2, "st");
+ string.insert_str(0, "");
+ string.insert(0, '\x52');
+ string.insert(0, '\u{0052}');
+ let x: usize = 2;
+ string.insert(x, 'a');
+ const Y: usize = 1;
+ string.insert(Y, 'a');
+ string.insert(Y, '"');
+ string.insert(Y, '\'');
+
+ get_string!().insert(1, '?');
+}
diff --git a/tests/ui/single_char_add_str.rs b/tests/ui/single_char_add_str.rs
new file mode 100644
index 0000000..a799ea7
--- /dev/null
+++ b/tests/ui/single_char_add_str.rs
@@ -0,0 +1,45 @@
+// run-rustfix
+#![warn(clippy::single_char_add_str)]
+
+macro_rules! get_string {
+ () => {
+ String::from("Hello world!")
+ };
+}
+
+fn main() {
+ // `push_str` tests
+
+ let mut string = String::new();
+ string.push_str("R");
+ string.push_str("'");
+
+ string.push('u');
+ string.push_str("st");
+ string.push_str("");
+ string.push_str("\x52");
+ string.push_str("\u{0052}");
+ string.push_str(r##"a"##);
+
+ get_string!().push_str("ö");
+
+ // `insert_str` tests
+
+ let mut string = String::new();
+ string.insert_str(0, "R");
+ string.insert_str(1, "'");
+
+ string.insert(0, 'u');
+ string.insert_str(2, "st");
+ string.insert_str(0, "");
+ string.insert_str(0, "\x52");
+ string.insert_str(0, "\u{0052}");
+ let x: usize = 2;
+ string.insert_str(x, r##"a"##);
+ const Y: usize = 1;
+ string.insert_str(Y, r##"a"##);
+ string.insert_str(Y, r##"""##);
+ string.insert_str(Y, r##"'"##);
+
+ get_string!().insert_str(1, "?");
+}
diff --git a/tests/ui/single_char_add_str.stderr b/tests/ui/single_char_add_str.stderr
new file mode 100644
index 0000000..55d9158
--- /dev/null
+++ b/tests/ui/single_char_add_str.stderr
@@ -0,0 +1,94 @@
+error: calling `push_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:14:5
+ |
+LL | string.push_str("R");
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')`
+ |
+ = note: `-D clippy::single-char-add-str` implied by `-D warnings`
+
+error: calling `push_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:15:5
+ |
+LL | string.push_str("'");
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')`
+
+error: calling `push_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:20:5
+ |
+LL | string.push_str("/x52");
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')`
+
+error: calling `push_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:21:5
+ |
+LL | string.push_str("/u{0052}");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')`
+
+error: calling `push_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:22:5
+ |
+LL | string.push_str(r##"a"##);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')`
+
+error: calling `push_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:24:5
+ |
+LL | get_string!().push_str("ö");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:29:5
+ |
+LL | string.insert_str(0, "R");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:30:5
+ |
+LL | string.insert_str(1, "'");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:35:5
+ |
+LL | string.insert_str(0, "/x52");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:36:5
+ |
+LL | string.insert_str(0, "/u{0052}");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:38:5
+ |
+LL | string.insert_str(x, r##"a"##);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:40:5
+ |
+LL | string.insert_str(Y, r##"a"##);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:41:5
+ |
+LL | string.insert_str(Y, r##"""##);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '"')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:42:5
+ |
+LL | string.insert_str(Y, r##"'"##);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '/'')`
+
+error: calling `insert_str()` using a single-character string literal
+ --> $DIR/single_char_add_str.rs:44:5
+ |
+LL | get_string!().insert_str(1, "?");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')`
+
+error: aborting due to 15 previous errors
+
diff --git a/tests/ui/single_char_pattern.fixed b/tests/ui/single_char_pattern.fixed
index 3871c4f..d8b5f19 100644
--- a/tests/ui/single_char_pattern.fixed
+++ b/tests/ui/single_char_pattern.fixed
@@ -12,15 +12,9 @@
let y = "x";
x.split(y);
- // Not yet testing for multi-byte characters
- // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern`
- // should have done this but produced an ICE
- //
- // We may not want to suggest changing these anyway
- // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984
- x.split("ß");
- x.split("ℝ");
- x.split("💣");
+ x.split('ß');
+ x.split('ℝ');
+ x.split('💣');
// Can't use this lint for unicode code points which don't fit in a char
x.split("❤️");
x.contains('x');
diff --git a/tests/ui/single_char_pattern.rs b/tests/ui/single_char_pattern.rs
index 32afe33..a7bc73e 100644
--- a/tests/ui/single_char_pattern.rs
+++ b/tests/ui/single_char_pattern.rs
@@ -12,12 +12,6 @@
let y = "x";
x.split(y);
- // Not yet testing for multi-byte characters
- // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern`
- // should have done this but produced an ICE
- //
- // We may not want to suggest changing these anyway
- // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984
x.split("ß");
x.split("ℝ");
x.split("💣");
diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr
index fe7211c..ee4e7e5 100644
--- a/tests/ui/single_char_pattern.stderr
+++ b/tests/ui/single_char_pattern.stderr
@@ -7,160 +7,178 @@
= note: `-D clippy::single-char-pattern` implied by `-D warnings`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:26:16
+ --> $DIR/single_char_pattern.rs:15:13
+ |
+LL | x.split("ß");
+ | ^^^ help: try using a `char` instead: `'ß'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:16:13
+ |
+LL | x.split("ℝ");
+ | ^^^ help: try using a `char` instead: `'ℝ'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:17:13
+ |
+LL | x.split("💣");
+ | ^^^^ help: try using a `char` instead: `'💣'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:20:16
|
LL | x.contains("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:27:19
+ --> $DIR/single_char_pattern.rs:21:19
|
LL | x.starts_with("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:28:17
+ --> $DIR/single_char_pattern.rs:22:17
|
LL | x.ends_with("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:29:12
+ --> $DIR/single_char_pattern.rs:23:12
|
LL | x.find("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:30:13
+ --> $DIR/single_char_pattern.rs:24:13
|
LL | x.rfind("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:31:14
+ --> $DIR/single_char_pattern.rs:25:14
|
LL | x.rsplit("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:32:24
+ --> $DIR/single_char_pattern.rs:26:24
|
LL | x.split_terminator("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:33:25
+ --> $DIR/single_char_pattern.rs:27:25
|
LL | x.rsplit_terminator("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:34:17
+ --> $DIR/single_char_pattern.rs:28:17
|
LL | x.splitn(0, "x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:35:18
+ --> $DIR/single_char_pattern.rs:29:18
|
LL | x.rsplitn(0, "x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:36:15
+ --> $DIR/single_char_pattern.rs:30:15
|
LL | x.matches("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:37:16
+ --> $DIR/single_char_pattern.rs:31:16
|
LL | x.rmatches("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:38:21
+ --> $DIR/single_char_pattern.rs:32:21
|
LL | x.match_indices("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:39:22
+ --> $DIR/single_char_pattern.rs:33:22
|
LL | x.rmatch_indices("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:40:26
+ --> $DIR/single_char_pattern.rs:34:26
|
LL | x.trim_start_matches("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:41:24
+ --> $DIR/single_char_pattern.rs:35:24
|
LL | x.trim_end_matches("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:43:13
+ --> $DIR/single_char_pattern.rs:37:13
|
LL | x.split("/n");
| ^^^^ help: try using a `char` instead: `'/n'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:44:13
+ --> $DIR/single_char_pattern.rs:38:13
|
LL | x.split("'");
| ^^^ help: try using a `char` instead: `'/''`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:45:13
+ --> $DIR/single_char_pattern.rs:39:13
|
LL | x.split("/'");
| ^^^^ help: try using a `char` instead: `'/''`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:50:31
+ --> $DIR/single_char_pattern.rs:44:31
|
LL | x.replace(";", ",").split(","); // issue #2978
| ^^^ help: try using a `char` instead: `','`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:51:19
+ --> $DIR/single_char_pattern.rs:45:19
|
LL | x.starts_with("/x03"); // issue #2996
| ^^^^^^ help: try using a `char` instead: `'/x03'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:58:13
+ --> $DIR/single_char_pattern.rs:52:13
|
LL | x.split(r"a");
| ^^^^ help: try using a `char` instead: `'a'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:59:13
+ --> $DIR/single_char_pattern.rs:53:13
|
LL | x.split(r#"a"#);
| ^^^^^^ help: try using a `char` instead: `'a'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:60:13
+ --> $DIR/single_char_pattern.rs:54:13
|
LL | x.split(r###"a"###);
| ^^^^^^^^^^ help: try using a `char` instead: `'a'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:61:13
+ --> $DIR/single_char_pattern.rs:55:13
|
LL | x.split(r###"'"###);
| ^^^^^^^^^^ help: try using a `char` instead: `'/''`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:62:13
+ --> $DIR/single_char_pattern.rs:56:13
|
LL | x.split(r###"#"###);
| ^^^^^^^^^^ help: try using a `char` instead: `'#'`
-error: aborting due to 27 previous errors
+error: aborting due to 30 previous errors
diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed
deleted file mode 100644
index 0812c02..0000000
--- a/tests/ui/single_char_push_str.fixed
+++ /dev/null
@@ -1,15 +0,0 @@
-// run-rustfix
-#![warn(clippy::single_char_push_str)]
-
-fn main() {
- let mut string = String::new();
- string.push('R');
- string.push('\'');
-
- string.push('u');
- string.push_str("st");
- string.push_str("");
- string.push('\x52');
- string.push('\u{0052}');
- string.push('a');
-}
diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs
deleted file mode 100644
index ab293bb..0000000
--- a/tests/ui/single_char_push_str.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-// run-rustfix
-#![warn(clippy::single_char_push_str)]
-
-fn main() {
- let mut string = String::new();
- string.push_str("R");
- string.push_str("'");
-
- string.push('u');
- string.push_str("st");
- string.push_str("");
- string.push_str("\x52");
- string.push_str("\u{0052}");
- string.push_str(r##"a"##);
-}
diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr
deleted file mode 100644
index 0e9bdaa..0000000
--- a/tests/ui/single_char_push_str.stderr
+++ /dev/null
@@ -1,34 +0,0 @@
-error: calling `push_str()` using a single-character string literal
- --> $DIR/single_char_push_str.rs:6:5
- |
-LL | string.push_str("R");
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')`
- |
- = note: `-D clippy::single-char-push-str` implied by `-D warnings`
-
-error: calling `push_str()` using a single-character string literal
- --> $DIR/single_char_push_str.rs:7:5
- |
-LL | string.push_str("'");
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')`
-
-error: calling `push_str()` using a single-character string literal
- --> $DIR/single_char_push_str.rs:12:5
- |
-LL | string.push_str("/x52");
- | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')`
-
-error: calling `push_str()` using a single-character string literal
- --> $DIR/single_char_push_str.rs:13:5
- |
-LL | string.push_str("/u{0052}");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')`
-
-error: calling `push_str()` using a single-character string literal
- --> $DIR/single_char_push_str.rs:14:5
- |
-LL | string.push_str(r##"a"##);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')`
-
-error: aborting due to 5 previous errors
-
diff --git a/tests/ui/skip_while_next.stderr b/tests/ui/skip_while_next.stderr
index a6b7bcd..269cc13 100644
--- a/tests/ui/skip_while_next.stderr
+++ b/tests/ui/skip_while_next.stderr
@@ -1,13 +1,13 @@
-error: called `skip_while(p).next()` on an `Iterator`
+error: called `skip_while(<p>).next()` on an `Iterator`
--> $DIR/skip_while_next.rs:14:13
|
LL | let _ = v.iter().skip_while(|&x| *x < 0).next();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::skip-while-next` implied by `-D warnings`
- = help: this is more succinctly expressed by calling `.find(!p)` instead
+ = help: this is more succinctly expressed by calling `.find(!<p>)` instead
-error: called `skip_while(p).next()` on an `Iterator`
+error: called `skip_while(<p>).next()` on an `Iterator`
--> $DIR/skip_while_next.rs:17:13
|
LL | let _ = v.iter().skip_while(|&x| {
@@ -17,7 +17,7 @@
LL | | ).next();
| |___________________________^
|
- = help: this is more succinctly expressed by calling `.find(!p)` instead
+ = help: this is more succinctly expressed by calling `.find(!<p>)` instead
error: aborting due to 2 previous errors
diff --git a/tests/ui/toplevel_ref_arg.fixed b/tests/ui/toplevel_ref_arg.fixed
index 33605ac..b129d95 100644
--- a/tests/ui/toplevel_ref_arg.fixed
+++ b/tests/ui/toplevel_ref_arg.fixed
@@ -1,7 +1,17 @@
// run-rustfix
+// aux-build:macro_rules.rs
#![warn(clippy::toplevel_ref_arg)]
+#[macro_use]
+extern crate macro_rules;
+
+macro_rules! gen_binding {
+ () => {
+ let _y = &42;
+ };
+}
+
fn main() {
// Closures should not warn
let y = |ref x| println!("{:?}", x);
@@ -26,4 +36,15 @@
// ok
for ref _x in 0..10 {}
+
+ // lint in macro
+ #[allow(unused)]
+ {
+ gen_binding!();
+ }
+
+ // do not lint in external macro
+ {
+ ref_arg_binding!();
+ }
}
diff --git a/tests/ui/toplevel_ref_arg.rs b/tests/ui/toplevel_ref_arg.rs
index 59759f1..73eb4ff 100644
--- a/tests/ui/toplevel_ref_arg.rs
+++ b/tests/ui/toplevel_ref_arg.rs
@@ -1,7 +1,17 @@
// run-rustfix
+// aux-build:macro_rules.rs
#![warn(clippy::toplevel_ref_arg)]
+#[macro_use]
+extern crate macro_rules;
+
+macro_rules! gen_binding {
+ () => {
+ let ref _y = 42;
+ };
+}
+
fn main() {
// Closures should not warn
let y = |ref x| println!("{:?}", x);
@@ -26,4 +36,15 @@
// ok
for ref _x in 0..10 {}
+
+ // lint in macro
+ #[allow(unused)]
+ {
+ gen_binding!();
+ }
+
+ // do not lint in external macro
+ {
+ ref_arg_binding!();
+ }
}
diff --git a/tests/ui/toplevel_ref_arg.stderr b/tests/ui/toplevel_ref_arg.stderr
index 19d6949..15cb933 100644
--- a/tests/ui/toplevel_ref_arg.stderr
+++ b/tests/ui/toplevel_ref_arg.stderr
@@ -1,5 +1,5 @@
error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
- --> $DIR/toplevel_ref_arg.rs:10:9
+ --> $DIR/toplevel_ref_arg.rs:20:9
|
LL | let ref _x = 1;
| ----^^^^^^----- help: try: `let _x = &1;`
@@ -7,28 +7,39 @@
= note: `-D clippy::toplevel-ref-arg` implied by `-D warnings`
error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
- --> $DIR/toplevel_ref_arg.rs:12:9
+ --> $DIR/toplevel_ref_arg.rs:22:9
|
LL | let ref _y: (&_, u8) = (&1, 2);
| ----^^^^^^--------------------- help: try: `let _y: &(&_, u8) = &(&1, 2);`
error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
- --> $DIR/toplevel_ref_arg.rs:14:9
+ --> $DIR/toplevel_ref_arg.rs:24:9
|
LL | let ref _z = 1 + 2;
| ----^^^^^^--------- help: try: `let _z = &(1 + 2);`
error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
- --> $DIR/toplevel_ref_arg.rs:16:9
+ --> $DIR/toplevel_ref_arg.rs:26:9
|
LL | let ref mut _z = 1 + 2;
| ----^^^^^^^^^^--------- help: try: `let _z = &mut (1 + 2);`
error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
- --> $DIR/toplevel_ref_arg.rs:21:9
+ --> $DIR/toplevel_ref_arg.rs:31:9
|
LL | let ref _x = vec![1, 2, 3];
| ----^^^^^^----------------- help: try: `let _x = &vec![1, 2, 3];`
-error: aborting due to 5 previous errors
+error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
+ --> $DIR/toplevel_ref_arg.rs:11:13
+ |
+LL | let ref _y = 42;
+ | ----^^^^^^------ help: try: `let _y = &42;`
+...
+LL | gen_binding!();
+ | --------------- in this macro invocation
+ |
+ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 6 previous errors
diff --git a/tests/ui/toplevel_ref_arg_non_rustfix.rs b/tests/ui/toplevel_ref_arg_non_rustfix.rs
index 42cac2b..1a493fb 100644
--- a/tests/ui/toplevel_ref_arg_non_rustfix.rs
+++ b/tests/ui/toplevel_ref_arg_non_rustfix.rs
@@ -1,11 +1,33 @@
+// aux-build:macro_rules.rs
+
#![warn(clippy::toplevel_ref_arg)]
#![allow(unused)]
+#[macro_use]
+extern crate macro_rules;
+
fn the_answer(ref mut x: u8) {
*x = 42;
}
+macro_rules! gen_function {
+ () => {
+ fn fun_example(ref _x: usize) {}
+ };
+}
+
fn main() {
let mut x = 0;
the_answer(x);
+
+ // lint in macro
+ #[allow(unused)]
+ {
+ gen_function!();
+ }
+
+ // do not lint in external macro
+ {
+ ref_arg_function!();
+ }
}
diff --git a/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/tests/ui/toplevel_ref_arg_non_rustfix.stderr
index 295e2f3..6c36141 100644
--- a/tests/ui/toplevel_ref_arg_non_rustfix.stderr
+++ b/tests/ui/toplevel_ref_arg_non_rustfix.stderr
@@ -1,10 +1,21 @@
error: `ref` directly on a function argument is ignored. Consider using a reference type instead.
- --> $DIR/toplevel_ref_arg_non_rustfix.rs:4:15
+ --> $DIR/toplevel_ref_arg_non_rustfix.rs:9:15
|
LL | fn the_answer(ref mut x: u8) {
| ^^^^^^^^^
|
= note: `-D clippy::toplevel-ref-arg` implied by `-D warnings`
-error: aborting due to previous error
+error: `ref` directly on a function argument is ignored. Consider using a reference type instead.
+ --> $DIR/toplevel_ref_arg_non_rustfix.rs:15:24
+ |
+LL | fn fun_example(ref _x: usize) {}
+ | ^^^^^^
+...
+LL | gen_function!();
+ | ---------------- in this macro invocation
+ |
+ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
diff --git a/tests/ui/unreadable_literal.fixed b/tests/ui/unreadable_literal.fixed
index 3f358d9..4043d53 100644
--- a/tests/ui/unreadable_literal.fixed
+++ b/tests/ui/unreadable_literal.fixed
@@ -14,7 +14,7 @@
let _good = (
0b1011_i64,
0o1_234_u32,
- 0x1_234_567,
+ 0x0123_4567,
65536,
1_2345_6789,
1234_f32,
diff --git a/tests/ui/unreadable_literal.stderr b/tests/ui/unreadable_literal.stderr
index 1b2ff6b..8645cab 100644
--- a/tests/ui/unreadable_literal.stderr
+++ b/tests/ui/unreadable_literal.stderr
@@ -1,3 +1,11 @@
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/unreadable_literal.rs:17:9
+ |
+LL | 0x1_234_567,
+ | ^^^^^^^^^^^ help: consider: `0x0123_4567`
+ |
+ = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
+
error: long literal lacking separators
--> $DIR/unreadable_literal.rs:25:17
|
@@ -54,5 +62,5 @@
LL | let _fail12: i128 = 0xabcabcabcabcabcabc;
| ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc`
-error: aborting due to 9 previous errors
+error: aborting due to 10 previous errors
diff --git a/triagebot.toml b/triagebot.toml
index ed3c83a..b7b20b4 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -1,7 +1,7 @@
[relabel]
allow-unauthenticated = [
- "C-*", "A-*", "E-*", "L-*", "M-*", "O-*", "S-*",
- "good first issue", "needs test"
+ "A-*", "C-*", "E-*", "L-*", "M-*", "O-*", "P-*", "S-*", "T-*",
+ "good first issue"
]
[assign]