diff --git a/src/tools/clippy/.github/driver.sh b/src/tools/clippy/.github/driver.sh index 321e00df153b6..6ff189fc85926 100644 --- a/src/tools/clippy/.github/driver.sh +++ b/src/tools/clippy/.github/driver.sh @@ -24,16 +24,16 @@ unset CARGO_MANIFEST_DIR # 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/double_neg.rs 2>double_neg.stderr && exit 1 sed -e "s,tests/ui,\$DIR," -e "/= help/d" double_neg.stderr >normalized.stderr -diff normalized.stderr tests/ui/double_neg.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) +diff -u <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose) 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) +diff -u <($CLIPPY) <($RUSTC) # TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 25f3b5da198af..1b9b33803de26 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -22,7 +22,7 @@ Current beta, release 2020-11-19 * [`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 @@ Released 2018-09-13 [`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 @@ Released 2018-09-13 [`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 @@ Released 2018-09-13 [`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 @@ Released 2018-09-13 [`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 @@ Released 2018-09-13 [`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 @@ Released 2018-09-13 [`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 @@ Released 2018-09-13 [`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 diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index 6494695606c3a..a8e2123656e97 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -63,9 +63,10 @@ To figure out how this syntax structure is encoded in the AST, it is recommended 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/src/tools/clippy/README.md b/src/tools/clippy/README.md index e1b3c84d6917d..8a5975e1f9711 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -167,18 +167,21 @@ You can add options to your code to `allow`/`warn`/`deny` Clippy lints: * `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 @@ can run Clippy with warnings for all lints enabled: 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/src/tools/clippy/clippy_dev/src/ra_setup.rs b/src/tools/clippy/clippy_dev/src/ra_setup.rs index c67efc10f1578..9d9e836cc08a2 100644 --- a/src/tools/clippy/clippy_dev/src/ra_setup.rs +++ b/src/tools/clippy/clippy_dev/src/ra_setup.rs @@ -11,7 +11,7 @@ use std::path::PathBuf; // 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 @@ fn inject_deps_into_manifest( 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/src/tools/clippy/clippy_lints/src/arithmetic.rs b/src/tools/clippy/clippy_lints/src/arithmetic.rs index e84481f9b5385..9861d8cfc4e5f 100644 --- a/src/tools/clippy/clippy_lints/src/arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/arithmetic.rs @@ -88,9 +88,28 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic { 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/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs index f6eadbdef0bb0..a004abb58b83e 100644 --- a/src/tools/clippy/clippy_lints/src/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -16,6 +16,7 @@ use rustc_middle::lint::in_external_macro; 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 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { }, _ => {}, } - 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 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } 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 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { 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 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMet 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 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut 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_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It 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 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { } 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/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 280a2c7fe6770..90bb0bd555f27 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -11,6 +11,7 @@ use rustc_lint::{LateContext, LateLintPass}; 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 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { }, 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/src/tools/clippy/clippy_lints/src/bytecount.rs b/src/tools/clippy/clippy_lints/src/bytecount.rs index d7d02ebf985c8..38a0e27c4cf5b 100644 --- a/src/tools/clippy/clippy_lints/src/bytecount.rs +++ b/src/tools/clippy/clippy_lints/src/bytecount.rs @@ -8,6 +8,7 @@ use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp}; 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 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { 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/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 14ef8c319eff1..b1bc2ec29e16e 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; 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 @@ impl CognitiveComplexity { 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 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { 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/src/tools/clippy/clippy_lints/src/consts.rs b/src/tools/clippy/clippy_lints/src/consts.rs index c8bbc9ce2b028..0035ded9356cf 100644 --- a/src/tools/clippy/clippy_lints/src/consts.rs +++ b/src/tools/clippy/clippy_lints/src/consts.rs @@ -7,10 +7,10 @@ use rustc_data_structures::sync::Lrc; 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, ScalarInt}; +use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_middle::mir::interpret::Scalar; use rustc_span::symbol::Symbol; use std::cmp::Ordering::{self, Equal}; use std::convert::TryInto; @@ -501,7 +501,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } pub fn miri_to_const(result: &ty::Const<'_>) -> Option { - use rustc_middle::mir::interpret::{ConstValue}; + use rustc_middle::mir::interpret::ConstValue; match result.val { ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => { match result.ty.kind() { diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs new file mode 100644 index 0000000000000..612c5355338a1 --- /dev/null +++ b/src/tools/clippy/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, +} + +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 ::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::)>>(); + + // 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::>() + .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 { + 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/src/tools/clippy/clippy_lints/src/default_trait_access.rs b/src/tools/clippy/clippy_lints/src/default_trait_access.rs deleted file mode 100644 index 3048436d9a7b5..0000000000000 --- a/src/tools/clippy/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 ::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/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 07f604cf71472..edecba57e44f0 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -15,7 +15,7 @@ use rustc_parse::maybe_new_parser_from_source_str; 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 @@ fn lint_for_missing_headers<'tcx>( ); } 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 @@ fn lint_for_missing_headers<'tcx>( 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 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs 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 @@ fn check_code(cx: &LateContext<'_>, text: &str, span: Span) { | 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/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 0240e80d81418..f8038d06e5034 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; 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 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { 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/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs index a9e05fddbe762..fe817fe94f2e4 100644 --- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -6,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::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 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { let reciever_ty = 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 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h 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/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 26da058598e4f..8e41e0e34daf7 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -10,6 +10,7 @@ use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, MatchSource, PatKind}; 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 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & 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 @@ fn check_unformatted(expr: &Expr<'_>) -> bool { 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/src/tools/clippy/clippy_lints/src/functions.rs b/src/tools/clippy/clippy_lints/src/functions.rs index 9c0efef95de8a..8b58d1f26013a 100644 --- a/src/tools/clippy/clippy_lints/src/functions.rs +++ b/src/tools/clippy/clippy_lints/src/functions.rs @@ -16,6 +16,7 @@ use rustc_middle::lint::in_external_macro; 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 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span 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/src/tools/clippy/clippy_lints/src/get_last_with_len.rs b/src/tools/clippy/clippy_lints/src/get_last_with_len.rs index 48ebcf5ebcd9c..cdd8a42e7cd12 100644 --- a/src/tools/clippy/clippy_lints/src/get_last_with_len.rs +++ b/src/tools/clippy/clippy_lints/src/get_last_with_len.rs @@ -8,6 +8,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for using `x.get(x.len() - 1)` instead of @@ -55,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for GetLastWithLen { // 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/src/tools/clippy/clippy_lints/src/if_let_some_result.rs b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs index 28b20cdeac343..e0a1f4c5ca4f2 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_some_result.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs @@ -4,6 +4,7 @@ use rustc_errors::Applicability; 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 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet { if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = op.kind; //check is expr.ok() has type Result.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/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs index 0877b44d90138..b723d06a6881d 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs @@ -2,6 +2,7 @@ use if_chain::if_chain; 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 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { 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/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs index 4b605fdb366a9..d1c3fdc71461b 100644 --- a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs +++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; 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 @@ impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody { 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/src/tools/clippy/clippy_lints/src/items_after_statements.rs b/src/tools/clippy/clippy_lints/src/items_after_statements.rs index c8576bcfcb444..8998fae09de31 100644 --- a/src/tools/clippy/clippy_lints/src/items_after_statements.rs +++ b/src/tools/clippy/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 @@ declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]); 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 @@ impl EarlyLintPass for ItemsAfterStatements { // 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/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index c9c4891bb08ac..8e2f03d6e4e91 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -68,7 +68,44 @@ declare_clippy_lint! { "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 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_> } 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_len( } } +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/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index e97fa543a093d..126852df502eb 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -176,7 +176,7 @@ mod copies; mod copy_iterator; mod create_dir; mod dbg_macro; -mod default_trait_access; +mod default; mod dereference; mod derive; mod disallowed_method; @@ -234,6 +234,7 @@ mod macro_use; 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; @@ -294,6 +295,7 @@ mod redundant_closure_call; mod redundant_field_names; mod redundant_pub_crate; mod redundant_static_lifetimes; +mod ref_option_ref; mod reference; mod regex; mod repeat_once; @@ -537,7 +539,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ©_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, @@ -615,6 +618,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -649,6 +653,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &main_recursion::MAIN_RECURSION, &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, + &manual_ok_or::MANUAL_OK_OR, &manual_strip::MANUAL_STRIP, &manual_unwrap_or::MANUAL_UNWRAP_OR, &map_clone::MAP_CLONE, @@ -692,6 +697,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -702,6 +708,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -712,8 +719,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -807,6 +814,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1030,6 +1038,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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); @@ -1047,7 +1056,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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); @@ -1098,6 +1106,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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); @@ -1146,6 +1155,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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); @@ -1210,7 +1220,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&checked_conversions::CHECKED_CONVERSIONS), 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), @@ -1234,6 +1244,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), LintId::of(&loops::EXPLICIT_ITER_LOOP), LintId::of(¯o_use::MACRO_USE_IMPORTS), + LintId::of(&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), @@ -1258,6 +1269,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1319,6 +1331,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1366,6 +1379,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1419,6 +1433,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1427,6 +1442,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1435,8 +1451,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1577,6 +1593,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1592,6 +1609,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1615,18 +1633,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1953,6 +1973,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { 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/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index c8a5a9c943135..4d737b3f49b03 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -16,7 +16,6 @@ use rustc_middle::hir::map::Map; 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 @@ fn could_use_elision<'tcx>( } 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/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs index 5d4d3dbcff45e..32c2562ee95b4 100644 --- a/src/tools/clippy/clippy_lints/src/loops.rs +++ b/src/tools/clippy/clippy_lints/src/loops.rs @@ -619,9 +619,9 @@ impl<'tcx> LateLintPass<'tcx> for Loops { } 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 @@ fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { _ => 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 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(& if let Some(self_expr) = args.get(0); if let Some(pushed_item) = args.get(1); // Check that the method being called is push() on a Vec - if 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 @@ fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: /// 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 @@ fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { 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 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { 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 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { // 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 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont 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)) { diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 9c623821fdddc..a1450b0d5feaa 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -5,7 +5,7 @@ use rustc_attr as attr; 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 check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants } 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 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants "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 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: "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/src/tools/clippy/clippy_lints/src/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs new file mode 100644 index 0000000000000..c99d2e35b94a6 --- /dev/null +++ b/src/tools/clippy/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 = None; + /// foo.map_or(Err("error"), |v| Ok(v)); + /// + /// let foo: Option = None; + /// foo.map_or(Err("error"), |v| Ok(v)); + /// ``` + /// + /// Use instead: + /// ```rust + /// let foo: Option = 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/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs index 22aa37e41fec0..9e2c6c7f231da 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs @@ -8,6 +8,7 @@ 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}; +use rustc_span::sym; declare_clippy_lint! { /// **What it does:** @@ -97,9 +98,9 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { 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/src/tools/clippy/clippy_lints/src/map_clone.rs b/src/tools/clippy/clippy_lints/src/map_clone.rs index 6d1c2ffbfbdd2..034cd99a9be06 100644 --- a/src/tools/clippy/clippy_lints/src/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/map_clone.rs @@ -10,7 +10,7 @@ use rustc_middle::mir::Mutability; 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 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { 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/src/tools/clippy/clippy_lints/src/map_identity.rs b/src/tools/clippy/clippy_lints/src/map_identity.rs index d4c2e66ff4b1e..6b782385a38d2 100644 --- a/src/tools/clippy/clippy_lints/src/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/map_identity.rs @@ -7,6 +7,7 @@ use rustc_errors::Applicability; 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 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a 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/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index 076ef235b8bd8..e50d11a4d7175 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -6,6 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; 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 @@ fn lint_map_unit_fn(cx: &LateContext<'_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr 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/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs b/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs index 331b6c6c34a94..086dae9422f9b 100644 --- a/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs +++ b/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs @@ -5,6 +5,7 @@ use rustc_hir::{Expr, ExprKind, LangItem, MatchSource}; 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_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opti fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); let ty = 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/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs index 4bdfca1a2921d..c6dca54e2509a 100644 --- a/src/tools/clippy/clippy_lints/src/matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches.rs @@ -22,7 +22,7 @@ use rustc_middle::lint::in_external_macro; 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::Symbol; +use rustc_span::{sym, Symbol}; use std::cmp::Ordering; use std::collections::hash_map::Entry; use std::collections::Bound; @@ -662,7 +662,7 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp } } else { // not a block, don't lint - return; + return; }; let ty = cx.typeck_results().expr_ty(ex); @@ -840,7 +840,7 @@ fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let ex_ty = 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)); @@ -1509,6 +1509,7 @@ mod redundant_pattern_match { 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 { @@ -1552,7 +1553,7 @@ mod redundant_pattern_match { 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; diff --git a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs index 5dae7efad9763..c83b6f2c32963 100644 --- a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs @@ -7,6 +7,7 @@ use rustc_errors::Applicability; 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 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { 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/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index d250bfd71e936..7186656f4e11a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -32,8 +32,7 @@ use crate::utils::{ 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! { @@ -1291,8 +1290,8 @@ declare_clippy_lint! { } 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. /// @@ -1301,16 +1300,18 @@ declare_clippy_lint! { /// **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! { @@ -1349,6 +1350,60 @@ declare_clippy_lint! { "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" } +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.map(_).collect::()`. + /// + /// **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::>(); + /// ``` + /// Use instead: + /// ```rust + /// (0..3).try_for_each(|t| Err(t)); + /// ``` + pub MAP_COLLECT_RESULT_UNIT, + style, + "using `.map(_).collect::()`, 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 = 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, @@ -1370,7 +1425,7 @@ declare_lint_pass!(Methods => [ INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, - SINGLE_CHAR_PUSH_STR, + SINGLE_CHAR_ADD_STR, SEARCH_IS_SOME, FILTER_NEXT, SKIP_WHILE_NEXT, @@ -1398,6 +1453,8 @@ declare_lint_pass!(Methods => [ 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 { @@ -1479,10 +1536,18 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["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); @@ -1499,6 +1564,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { 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); } } @@ -1655,7 +1722,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { 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(); @@ -1712,7 +1779,7 @@ fn lint_or_fun_call<'tcx>( "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, ); @@ -1745,7 +1812,7 @@ fn lint_or_fun_call<'tcx>( _ => (), } - if is_type_diagnostic_item(cx, ty, sym!(vec_type)) { + if is_type_diagnostic_item(cx, ty, sym::vec_type) { return; } } @@ -1842,11 +1909,11 @@ fn lint_expect_fun_call( 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] @@ -1864,7 +1931,7 @@ fn lint_expect_fun_call( // 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() { @@ -1951,9 +2018,9 @@ fn lint_expect_fun_call( } 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; @@ -2119,7 +2186,7 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir:: return; }; - let snippet = snippet_with_macro_callsite(cx, arg.span, "_"); + let snippet = snippet_with_macro_callsite(cx, arg.span, ".."); span_lint_and_sugg( cx, @@ -2140,7 +2207,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E 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; @@ -2155,9 +2222,9 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E "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, ); @@ -2166,14 +2233,14 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let obj_ty = 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_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()); @@ -2326,7 +2393,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ ); } } - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym!(vec_type)) + } 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(_, _) @@ -2359,7 +2426,7 @@ fn lint_iter_nth<'tcx>( 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" @@ -2404,7 +2471,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: 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 }; @@ -2412,7 +2479,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() { needs_ref = get_args_str.parse::().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::().is_ok(); "Vec" } else if is_type_diagnostic_item(cx, expr_ty, sym!(vecdeque_type)) { @@ -2464,7 +2531,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: 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, @@ -2480,7 +2547,7 @@ fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[ 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, @@ -2498,7 +2565,7 @@ fn derefs_to_slice<'tcx>( 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), @@ -2508,7 +2575,7 @@ fn derefs_to_slice<'tcx>( } 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 @@ -2533,9 +2600,9 @@ fn derefs_to_slice<'tcx>( 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 @@ -2585,7 +2652,7 @@ fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::E 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); @@ -2615,7 +2682,7 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map _ => 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, }; @@ -2641,7 +2708,7 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } // 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( @@ -2665,8 +2732,8 @@ fn lint_map_unwrap_or_else<'tcx>( 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 @@ -2683,11 +2750,11 @@ fn lint_map_unwrap_or_else<'tcx>( // 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().unwrap_or_else()` on an `Option` value. This can be done more directly by calling \ + `map_or_else(, )` 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().unwrap_or_else()` on a `Result` value. This can be done more directly by calling \ + `.map_or_else(, )` instead" }; // get snippets for args to map() and unwrap_or_else() let map_snippet = snippet(cx, map_args[1].span, ".."); @@ -2697,16 +2764,15 @@ fn lint_map_unwrap_or_else<'tcx>( 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 { @@ -2720,8 +2786,8 @@ fn lint_map_unwrap_or_else<'tcx>( /// 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` to `Option` @@ -2753,8 +2819,8 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map 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, @@ -2792,18 +2858,20 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map 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); @@ -2823,9 +2891,9 @@ fn lint_skip_while_next<'tcx>( cx, SKIP_WHILE_NEXT, expr.span, - "called `skip_while(p).next()` on an `Iterator`", + "called `skip_while(

).next()` on an `Iterator`", None, - "this is more succinctly expressed by calling `.find(!p)` instead", + "this is more succinctly expressed by calling `.find(!

)` instead", ); } } @@ -2839,7 +2907,7 @@ fn lint_filter_map<'tcx>( ) { // 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); } @@ -2848,17 +2916,19 @@ fn lint_filter_map<'tcx>( /// 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); @@ -2875,7 +2945,7 @@ fn lint_find_map<'tcx>( ) { // 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); } @@ -2890,7 +2960,7 @@ fn lint_filter_map_map<'tcx>( ) { // 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); } @@ -2905,7 +2975,7 @@ fn lint_filter_flat_map<'tcx>( ) { // 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); @@ -2921,7 +2991,7 @@ fn lint_filter_map_flat_map<'tcx>( ) { // 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); @@ -3075,7 +3145,7 @@ fn lint_chars_cmp( 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(); @@ -3092,9 +3162,9 @@ fn lint_chars_cmp( "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, ); @@ -3141,7 +3211,7 @@ fn lint_chars_cmp_with_unwrap<'tcx>( "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, @@ -3177,7 +3247,7 @@ fn get_hint_if_single_char_arg( 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 { @@ -3216,11 +3286,12 @@ fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &h 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", @@ -3230,6 +3301,26 @@ fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args } } +/// 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" @@ -3259,7 +3350,7 @@ fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_re 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, ); } @@ -3350,7 +3441,7 @@ fn lint_option_as_ref_deref<'tcx>( 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; } @@ -3445,10 +3536,46 @@ fn lint_option_as_ref_deref<'tcx>( } } +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` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { 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, } } @@ -3770,6 +3897,28 @@ fn lint_filetype_is_file(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir 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/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs index 95fa28e1c0f75..7763fd5f113fa 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -7,7 +7,7 @@ use rustc_hir::{self, HirId, Path}; 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 @@ pub(super) fn lint<'tcx>( 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 @@ pub(super) fn lint<'tcx>( // 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 { "" }; let unwrap_snippet_none = unwrap_snippet == "None"; let suggest = if unwrap_snippet_none { - "and_then(f)" + "and_then()" } else { - "map_or(a, f)" + "map_or(, )" }; let msg = &format!( - "called `map(f).unwrap_or({})` on an `Option` value. \ + "called `map().unwrap_or({})` on an `Option` value. \ This can be done more directly by calling `{}` instead", arg, suggest ); diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 08b3eab9b7cdf..cde89983a2656 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -3,6 +3,7 @@ use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::UNNECESSARY_LAZY_EVALUATIONS; @@ -14,8 +15,8 @@ pub(super) fn lint<'tcx>( 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/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index 909e79f661a6d..308e92057b751 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -7,6 +7,7 @@ use rustc_hir::{ 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 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { 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 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { 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 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { ("", 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 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { "try", format!( "let {name}{tyopt} = {initref};", - name=snippet(cx, name.span, "_"), + name=snippet(cx, name.span, ".."), tyopt=tyopt, initref=initref, ), diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 813f9c4394819..009e3d8937e02 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -14,6 +14,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; 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 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { 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 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { 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/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 3eae45b2819d8..53abe6086ea41 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -4,6 +4,7 @@ use rustc_hir as hir; 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 @@ declare_clippy_lint! { } 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/src/tools/clippy/clippy_lints/src/needless_borrow.rs b/src/tools/clippy/clippy_lints/src/needless_borrow.rs index b71d5496a37a3..405c21d608d97 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrow.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrow.rs @@ -10,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass}; 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 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } 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/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 2423eb4e6e394..1d0e230c6a777 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -13,7 +13,7 @@ use rustc_infer::infer::TyCtxtInferExt; 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 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { 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 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } } - 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 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { /// 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/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs index 28d1322e94626..68fdd0eb269ef 100644 --- a/src/tools/clippy/clippy_lints/src/new_without_default.rs +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -9,6 +9,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; 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<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // 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/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index 603440c0f8376..485888fa9444f 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -7,6 +7,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass}; 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 @@ impl EarlyLintPass for NonExpressiveNames { } 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/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index eb7624b25a3c9..681dbce97697e 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -8,6 +8,7 @@ use rustc_errors::Applicability; 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 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { path.ident.name.to_ident_string() == "ok" - && 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/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs index 4077aba6ef17d..72dfccc1089e9 100644 --- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs +++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs @@ -5,7 +5,7 @@ use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +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 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { 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/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs index 19d355e64ca86..ceecc8dbc06fc 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs @@ -3,6 +3,7 @@ use if_chain::if_chain; 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 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { 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/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index 28816c3076ddf..030650c3256c0 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -10,7 +10,7 @@ use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutabil 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 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } 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/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 6b1c848a9467b..dcb643a28aebc 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -15,7 +15,7 @@ use rustc_lint::{LateContext, LateLintPass}; 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 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } 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 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: }, ); } - } 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/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs index 0a2d1b5fbe6ac..e0996804a5934 100644 --- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs +++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs @@ -3,6 +3,7 @@ use rustc_errors::Applicability; 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 @@ fn expr_as_ptr_offset_call<'tcx>( ) -> 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/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index dbc676ae22408..d9b280b7a8597 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -4,6 +4,7 @@ use rustc_hir::def::{DefKind, Res}; 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 @@ impl QuestionMark { 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/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index de54711d8511b..79e9a56af9a19 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -7,6 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; 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 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: 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/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index ae3b0a037542a..b4a9804fb25a5 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -17,6 +17,7 @@ use rustc_middle::ty::{self, fold::TypeVisitor, Ty}; 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; @@ -115,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { 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) @@ -518,7 +519,10 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { self.possible_borrower.add(borrowed.local, lhs); }, other => { - if ContainsRegion.visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty).is_continue() { + if ContainsRegion + .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) + .is_continue() + { return; } rvalue_locals(other, |rhs| { diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs new file mode 100644 index 0000000000000..a914a77d48b4e --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/clippy_lints/src/repeat_once.rs b/src/tools/clippy/clippy_lints/src/repeat_once.rs index ae6013530091e..d34e744eb944c 100644 --- a/src/tools/clippy/clippy_lints/src/repeat_once.rs +++ b/src/tools/clippy/clippy_lints/src/repeat_once.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; 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 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { 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/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index a6e4252a0c825..7f4913a02cbd3 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -9,6 +9,7 @@ use rustc_middle::lint::in_external_macro; 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 @@ impl<'tcx> LateLintPass<'tcx> for Return { } 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/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 3783bd78de25e..0dd2da949c4c3 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -4,6 +4,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::source_map::Spanned; +use rustc_span::sym; use if_chain::if_chain; @@ -154,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { } 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/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 54b38d9f4ced2..386987eb181ea 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -8,6 +8,7 @@ use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind}; 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 @@ fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr< 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/src/tools/clippy/clippy_lints/src/try_err.rs b/src/tools/clippy/clippy_lints/src/try_err.rs index 3e747ec4ad9e2..6f6b6999bf0a9 100644 --- a/src/tools/clippy/clippy_lints/src/try_err.rs +++ b/src/tools/clippy/clippy_lints/src/try_err.rs @@ -9,6 +9,7 @@ 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::sym; declare_clippy_lint! { /// **What it does:** Checks for usages of `Err(x)?`. @@ -133,7 +134,7 @@ fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> O fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { 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 @@ fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option< 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 @@ fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> 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/src/tools/clippy/clippy_lints/src/types.rs b/src/tools/clippy/clippy_lints/src/types.rs index 6a33aaaaab204..c7d82da3b8ba4 100644 --- a/src/tools/clippy/clippy_lints/src/types.rs +++ b/src/tools/clippy/clippy_lints/src/types.rs @@ -10,9 +10,9 @@ use rustc_errors::{Applicability, DiagnosticBuilder}; 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 @@ impl Types { ); 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 @@ impl Types { 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 @@ impl Types { // 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`. 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 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool { false } +fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option> { + 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 @@ fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { 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 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't } 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 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't ); } } 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/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs index 1307237dbc70a..0bccfc156788a 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs @@ -6,6 +6,7 @@ use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegme 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 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { 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/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs index 7548c6afa973a..b1339c3d6395d 100644 --- a/src/tools/clippy/clippy_lints/src/unused_unit.rs +++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs @@ -123,6 +123,17 @@ fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { fn_source .rfind("->") .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + let mut rpos = rpos; + let chars: Vec = fn_source.chars().collect(); + while rpos > 1 { + if let Some(c) = chars.get(rpos - 1) { + if c.is_whitespace() { + rpos -= 1; + continue; + } + } + break; + } ( #[allow(clippy::cast_possible_truncation)] ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index ea4b8172c9c2e..f4a77e54dd149 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -11,6 +11,7 @@ use rustc_middle::lint::in_external_macro; 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 @@ fn collect_unwrap_info<'tcx>( invert: bool, ) -> Vec> { 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 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { 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/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs index 0f8797243eca3..fde3102933098 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs @@ -5,7 +5,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::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 @@ impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { // 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 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { // 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 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { // 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/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 4e4a206a583a2..c6194b0c6de3e 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -9,6 +9,7 @@ use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; 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 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { 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 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { 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/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index 6ca72d895c8da..8b59a9541a736 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -225,7 +225,7 @@ declare_clippy_lint! { /// /// 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, @@ -724,7 +724,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { 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, ); } diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index 0a8a4a5f9aedb..85e7f055e79de 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -52,6 +52,7 @@ use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; 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; @@ -974,7 +975,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { /// 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. diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs index 736a531eda657..1ad8c6029860b 100644 --- a/src/tools/clippy/clippy_lints/src/utils/paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/paths.rs @@ -44,6 +44,7 @@ pub const FN: [&str; 3] = ["core", "ops", "Fn"]; 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; 3] = ["core", "hash", "Hash"]; @@ -52,6 +53,7 @@ pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entr 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"]; diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index d9d60fffcd7ae..ff414f748ef97 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -10,8 +10,7 @@ use rustc_lexer::unescape::{self, EscapeError}; 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 @@ impl EarlyLintPass for Write { .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/src/tools/clippy/doc/adding_lints.md b/src/tools/clippy/doc/adding_lints.md index 2572833b8debf..b1dacfc9c6d28 100644 --- a/src/tools/clippy/doc/adding_lints.md +++ b/src/tools/clippy/doc/adding_lints.md @@ -311,7 +311,7 @@ Running our UI test should now produce output that contains the lint message. 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/tools/clippy/src/lintlist/mod.rs b/src/tools/clippy/src/lintlist/mod.rs index c2e63ecb581ec..702f9d86de62d 100644 --- a/src/tools/clippy/src/lintlist/mod.rs +++ b/src/tools/clippy/src/lintlist/mod.rs @@ -298,6 +298,13 @@ vec![ deprecation: None, 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", @@ -352,7 +359,7 @@ vec![ group: "pedantic", desc: "checks for literal calls to `Default::default()`", deprecation: None, - module: "default_trait_access", + module: "default", }, Lint { name: "deprecated_cfg_attr", @@ -620,6 +627,13 @@ vec![ deprecation: None, 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", @@ -746,6 +760,13 @@ vec![ deprecation: None, 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", @@ -1173,6 +1194,13 @@ vec![ deprecation: None, 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", @@ -1222,6 +1250,13 @@ vec![ deprecation: None, module: "map_clone", }, + Lint { + name: "map_collect_result_unit", + group: "style", + desc: "using `.map(_).collect::()`, which can be replaced with `try_for_each`", + deprecation: None, + module: "methods", + }, Lint { name: "map_entry", group: "perf", @@ -2013,6 +2048,13 @@ vec![ deprecation: None, 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", @@ -2133,16 +2175,16 @@ vec![ 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", }, diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs index 0bbb9534928ef..93303865e178e 100644 --- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs +++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs @@ -56,3 +56,17 @@ macro_rules! option_env_unwrap_external { 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/src/tools/clippy/tests/ui/borrow_box.rs b/src/tools/clippy/tests/ui/borrow_box.rs index 1901de46ca894..b606f773cfbad 100644 --- a/src/tools/clippy/tests/ui/borrow_box.rs +++ b/src/tools/clippy/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) { // Although this function could be changed to "&mut bool", // avoiding the Box, mutable references to boxes are not @@ -89,6 +91,20 @@ pub fn test13(boxed_slice: &mut Box<[i32]>) { *boxed_slice = data.into_boxed_slice(); } +// The suggestion should include proper parentheses to avoid a syntax error. +pub fn test14(_display: &Box) {} +pub fn test15(_display: &Box) {} +pub fn test16<'a>(_display: &'a Box) {} + +pub fn test17(_display: &Box) {} +pub fn test18(_display: &Box) {} +pub fn test19<'a>(_display: &'a Box) {} + +// 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/src/tools/clippy/tests/ui/borrow_box.stderr b/src/tools/clippy/tests/ui/borrow_box.stderr index b5db691f89f39..3eac32815be3f 100644 --- a/src/tools/clippy/tests/ui/borrow_box.stderr +++ b/src/tools/clippy/tests/ui/borrow_box.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:19:14 + --> $DIR/borrow_box.rs:21:14 | LL | let foo: &Box; | ^^^^^^^^^^ help: try: `&bool` @@ -11,16 +11,58 @@ LL | #![deny(clippy::borrowed_box)] | ^^^^^^^^^^^^^^^^^^^^ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:23:10 + --> $DIR/borrow_box.rs:25:10 | LL | foo: &'a Box, | ^^^^^^^^^^^^^ help: try: `&'a bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:27:17 + --> $DIR/borrow_box.rs:29:17 | LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` -error: aborting due to 3 previous errors +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:95:25 + | +LL | pub fn test14(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:96:25 + | +LL | pub fn test15(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:97:29 + | +LL | pub fn test16<'a>(_display: &'a Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:99:25 + | +LL | pub fn test17(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:100:25 + | +LL | pub fn test18(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:101:29 + | +LL | pub fn test19<'a>(_display: &'a Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)` + +error: you seem to be trying to use `&Box`. 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/src/tools/clippy/tests/ui/comparison_to_empty.fixed b/src/tools/clippy/tests/ui/comparison_to_empty.fixed new file mode 100644 index 0000000000000..261024caca761 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/comparison_to_empty.rs b/src/tools/clippy/tests/ui/comparison_to_empty.rs new file mode 100644 index 0000000000000..98ddd97495161 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/comparison_to_empty.stderr b/src/tools/clippy/tests/ui/comparison_to_empty.stderr new file mode 100644 index 0000000000000..f69d6bd5255df --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/crashes/ice-6250.rs b/src/tools/clippy/tests/ui/crashes/ice-6250.rs new file mode 100644 index 0000000000000..c33580ff6ab6a --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-6250.rs @@ -0,0 +1,16 @@ +// originally from glacier/fixed/77218.rs +// ice while adjusting... + +pub struct Cache { + data: Vec, +} + +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/src/tools/clippy/tests/ui/crashes/ice-6250.stderr b/src/tools/clippy/tests/ui/crashes/ice-6250.stderr new file mode 100644 index 0000000000000..8241dcd8feb7b --- /dev/null +++ b/src/tools/clippy/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, +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/src/tools/clippy/tests/ui/crashes/ice-6251.rs b/src/tools/clippy/tests/ui/crashes/ice-6251.rs new file mode 100644 index 0000000000000..6aa779aaeb3b6 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-6251.rs @@ -0,0 +1,6 @@ +// originally from glacier/fixed/77329.rs +// assertion failed: `(left == right) ; different DefIds + +fn bug() -> impl Iterator { + std::iter::empty() +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-6251.stderr b/src/tools/clippy/tests/ui/crashes/ice-6251.stderr new file mode 100644 index 0000000000000..9a7cf4b0919f6 --- /dev/null +++ b/src/tools/clippy/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() -> impl Iterator { +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() -> impl Iterator { + | ^ 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() -> impl Iterator { + | ^ + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/ice-6251.rs:4:54 + | +LL | fn bug() -> impl Iterator { + | ^ 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() -> impl Iterator { + | ^^^^^^^^^^^ 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/src/tools/clippy/tests/ui/crashes/ice-6252.rs b/src/tools/clippy/tests/ui/crashes/ice-6252.rs new file mode 100644 index 0000000000000..2e3d9fd1e9240 --- /dev/null +++ b/src/tools/clippy/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 { + const VAL: T; +} +struct Five; +struct Multiply { + _n: PhantomData, +} +impl TypeVal for Multiply where N: TypeVal {} + +fn main() { + [1; >::VAL]; +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr new file mode 100644 index 0000000000000..440973e24398e --- /dev/null +++ b/src/tools/clippy/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 TypeVal for Multiply where N: TypeVal {} + | - ^^^ 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 TypeVal for Multiply where N: TypeVal {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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; >::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/src/tools/clippy/tests/ui/crashes/ice-6254.rs b/src/tools/clippy/tests/ui/crashes/ice-6254.rs new file mode 100644 index 0000000000000..c19eca43884aa --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/crashes/ice-6254.stderr b/src/tools/clippy/tests/ui/crashes/ice-6254.stderr new file mode 100644 index 0000000000000..95ebf23d81817 --- /dev/null +++ b/src/tools/clippy/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 + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-6255.rs b/src/tools/clippy/tests/ui/crashes/ice-6255.rs new file mode 100644 index 0000000000000..bd4a81d98e2e7 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/crashes/ice-6255.stderr b/src/tools/clippy/tests/ui/crashes/ice-6255.stderr new file mode 100644 index 0000000000000..d973ea1e23a25 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/crashes/ice-6256.rs b/src/tools/clippy/tests/ui/crashes/ice-6256.rs new file mode 100644 index 0000000000000..6f60d45d68a8e --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/crashes/ice-6256.stderr b/src/tools/clippy/tests/ui/crashes/ice-6256.stderr new file mode 100644 index 0000000000000..0e8353a418a87 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/field_reassign_with_default.rs b/src/tools/clippy/tests/ui/field_reassign_with_default.rs new file mode 100644 index 0000000000000..79a30c22f9536 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/field_reassign_with_default.stderr b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr new file mode 100644 index 0000000000000..c788ebae5526c --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/filter_map_next.rs b/src/tools/clippy/tests/ui/filter_map_next.rs index f5d051be198ed..dbeb2354309c9 100644 --- a/src/tools/clippy/tests/ui/filter_map_next.rs +++ b/src/tools/clippy/tests/ui/filter_map_next.rs @@ -3,9 +3,6 @@ fn main() { let a = ["1", "lol", "3", "NaN", "5"]; - let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); - assert_eq!(element, Some(1)); - #[rustfmt::skip] let _: Option = vec![1, 2, 3, 4, 5, 6] .into_iter() diff --git a/src/tools/clippy/tests/ui/filter_map_next.stderr b/src/tools/clippy/tests/ui/filter_map_next.stderr index d69ae212414f6..45427684d96e1 100644 --- a/src/tools/clippy/tests/ui/filter_map_next.stderr +++ b/src/tools/clippy/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 = 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 = vec![1, 2, 3, 4, 5, 6] | __________________________^ @@ -19,6 +10,8 @@ LL | | if x == 2 { 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/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed b/src/tools/clippy/tests/ui/filter_map_next_fixable.fixed new file mode 100644 index 0000000000000..c3992d7e92cf3 --- /dev/null +++ b/src/tools/clippy/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 = a.iter().find_map(|s| s.parse().ok()); + assert_eq!(element, Some(1)); +} diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.rs b/src/tools/clippy/tests/ui/filter_map_next_fixable.rs new file mode 100644 index 0000000000000..447219a968391 --- /dev/null +++ b/src/tools/clippy/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 = a.iter().filter_map(|s| s.parse().ok()).next(); + assert_eq!(element, Some(1)); +} diff --git a/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr b/src/tools/clippy/tests/ui/filter_map_next_fixable.stderr new file mode 100644 index 0000000000000..6c2530e0379e4 --- /dev/null +++ b/src/tools/clippy/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 = 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/src/tools/clippy/tests/ui/filter_methods.stderr b/src/tools/clippy/tests/ui/filter_methods.stderr index 84a957a374c6b..91718dd11755b 100644 --- a/src/tools/clippy/tests/ui/filter_methods.stderr +++ b/src/tools/clippy/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 @@ LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * = 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 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = 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 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = 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/src/tools/clippy/tests/ui/find_map.stderr b/src/tools/clippy/tests/ui/find_map.stderr index f279850fef8af..aea3cc62afcc4 100644 --- a/src/tools/clippy/tests/ui/find_map.stderr +++ b/src/tools/clippy/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 = a.iter().find(|s| s.parse::().is_ok()).map(|s| s.parse().unwrap()); @@ -7,7 +7,7 @@ LL | let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s = 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 = desserts_of_the_week diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs new file mode 100644 index 0000000000000..045eb3133d3c4 --- /dev/null +++ b/src/tools/clippy/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::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); + + Vec::from_iter(vec![42u32]); +} diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.stderr new file mode 100644 index 0000000000000..46bdc2f4e199b --- /dev/null +++ b/src/tools/clippy/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::::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/src/tools/clippy/tests/ui/get_unwrap.fixed b/src/tools/clippy/tests/ui/get_unwrap.fixed index 97e6b20f471fc..924c02a4054d4 100644 --- a/src/tools/clippy/tests/ui/get_unwrap.fixed +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/get_unwrap.rs b/src/tools/clippy/tests/ui/get_unwrap.rs index 1c9a71c09699a..c0c37bb720663 100644 --- a/src/tools/clippy/tests/ui/get_unwrap.rs +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/integer_arithmetic.rs b/src/tools/clippy/tests/ui/integer_arithmetic.rs index 7b1b64f390a5a..b74c93dc4a666 100644 --- a/src/tools/clippy/tests/ui/integer_arithmetic.rs +++ b/src/tools/clippy/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 @@ fn main() { 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/src/tools/clippy/tests/ui/integer_arithmetic.stderr b/src/tools/clippy/tests/ui/integer_arithmetic.stderr index 83e8a9cde3ff1..add3b6b90fa26 100644 --- a/src/tools/clippy/tests/ui/integer_arithmetic.stderr +++ b/src/tools/clippy/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 @@ LL | 1 + i; = 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: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:36:5 + --> $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/src/tools/clippy/tests/ui/item_after_statement.rs b/src/tools/clippy/tests/ui/item_after_statement.rs index c17a7cbc8d905..377e58e441749 100644 --- a/src/tools/clippy/tests/ui/item_after_statement.rs +++ b/src/tools/clippy/tests/ui/item_after_statement.rs @@ -28,7 +28,10 @@ fn mac() { // 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/src/tools/clippy/tests/ui/item_after_statement.stderr b/src/tools/clippy/tests/ui/item_after_statement.stderr index f8f010b5e5c1f..68a3c81b6a804 100644 --- a/src/tools/clippy/tests/ui/item_after_statement.stderr +++ b/src/tools/clippy/tests/ui/item_after_statement.stderr @@ -16,5 +16,18 @@ LL | | println!("foo"); 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/src/tools/clippy/tests/ui/iter_skip_next.stderr b/src/tools/clippy/tests/ui/iter_skip_next.stderr index feedc2f288a2b..486de718bb563 100644 --- a/src/tools/clippy/tests/ui/iter_skip_next.stderr +++ b/src/tools/clippy/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 @@ LL | let _ = some_vec.iter().skip(42).next(); | = 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/src/tools/clippy/tests/ui/manual_ok_or.fixed b/src/tools/clippy/tests/ui/manual_ok_or.fixed new file mode 100644 index 0000000000000..b42e94bd727de --- /dev/null +++ b/src/tools/clippy/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 = None; + foo.ok_or("error"); + + // eta expansion case + foo.ok_or("error"); + + // turbo fish syntax + None::.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::(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/src/tools/clippy/tests/ui/manual_ok_or.rs b/src/tools/clippy/tests/ui/manual_ok_or.rs new file mode 100644 index 0000000000000..e5a6056fbf5cc --- /dev/null +++ b/src/tools/clippy/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 = None; + foo.map_or(Err("error"), |v| Ok(v)); + + // eta expansion case + foo.map_or(Err("error"), Ok); + + // turbo fish syntax + None::.map_or(Err("error"), |v| Ok(v)); + + // multiline case + #[rustfmt::skip] + foo.map_or(Err::( + &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::(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/src/tools/clippy/tests/ui/manual_ok_or.stderr b/src/tools/clippy/tests/ui/manual_ok_or.stderr new file mode 100644 index 0000000000000..8ea10ac543636 --- /dev/null +++ b/src/tools/clippy/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::.map_or(Err("error"), |v| Ok(v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `None::.ok_or("error")` + +error: this pattern reimplements `Option::ok_or` + --> $DIR/manual_ok_or.rs:21:5 + | +LL | / foo.map_or(Err::( +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/src/tools/clippy/tests/ui/map_collect_result_unit.fixed b/src/tools/clippy/tests/ui/map_collect_result_unit.fixed new file mode 100644 index 0000000000000..e66c9cc24207a --- /dev/null +++ b/src/tools/clippy/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::, _>>(); + let _ = (0..3).map(|t| Err(t + 1)).collect::>>(); +} diff --git a/src/tools/clippy/tests/ui/map_collect_result_unit.rs b/src/tools/clippy/tests/ui/map_collect_result_unit.rs new file mode 100644 index 0000000000000..6f08f4c3c5354 --- /dev/null +++ b/src/tools/clippy/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::>(); + 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::, _>>(); + let _ = (0..3).map(|t| Err(t + 1)).collect::>>(); +} diff --git a/src/tools/clippy/tests/ui/map_collect_result_unit.stderr b/src/tools/clippy/tests/ui/map_collect_result_unit.stderr new file mode 100644 index 0000000000000..8b06e13baa6b4 --- /dev/null +++ b/src/tools/clippy/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::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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/src/tools/clippy/tests/ui/map_unwrap_or.rs b/src/tools/clippy/tests/ui/map_unwrap_or.rs index 585944032e70d..87e16f5d09bd7 100644 --- a/src/tools/clippy/tests/ui/map_unwrap_or.rs +++ b/src/tools/clippy/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 @@ fn option_methods() { 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 @@ fn option_methods() { .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 = 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/src/tools/clippy/tests/ui/map_unwrap_or.stderr b/src/tools/clippy/tests/ui/map_unwrap_or.stderr index b62080a073f35..96b9d6cc3c145 100644 --- a/src/tools/clippy/tests/ui/map_unwrap_or.stderr +++ b/src/tools/clippy/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().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead + --> $DIR/map_unwrap_or.rs:16:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -8,13 +8,13 @@ LL | | .unwrap_or(0); | |_____________________^ | = note: `-D clippy::map-unwrap-or` implied by `-D warnings` -help: use `map_or(a, f)` instead +help: use `map_or(, )` 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().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead + --> $DIR/map_unwrap_or.rs:20:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -23,7 +23,7 @@ LL | | } LL | | ).unwrap_or(0); | |__________________^ | -help: use `map_or(a, f)` instead +help: use `map_or(, )` instead | LL | let _ = opt.map_or(0, |x| { LL | x + 1 @@ -31,8 +31,8 @@ LL | } 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().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead + --> $DIR/map_unwrap_or.rs:24:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -41,26 +41,26 @@ LL | | 0 LL | | }); | |__________^ | -help: use `map_or(a, f)` instead +help: use `map_or(, )` 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().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` 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()` 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().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead + --> $DIR/map_unwrap_or.rs:31:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -69,7 +69,7 @@ LL | | } LL | | ).unwrap_or(None); | |_____________________^ | -help: use `and_then(f)` instead +help: use `and_then()` instead | LL | let _ = opt.and_then(|x| { LL | Some(x + 1) @@ -77,8 +77,8 @@ LL | } 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().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead + --> $DIR/map_unwrap_or.rs:35:13 | LL | let _ = opt | _____________^ @@ -86,35 +86,24 @@ LL | | .map(|x| Some(x + 1)) LL | | .unwrap_or(None); | |________________________^ | -help: use `and_then(f)` instead +help: use `and_then()` 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().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` 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(, )` 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().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead + --> $DIR/map_unwrap_or.rs:50:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -123,8 +112,8 @@ LL | | } 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().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead + --> $DIR/map_unwrap_or.rs:54:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -133,29 +122,25 @@ 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:88:13 +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` 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)` - -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 - | -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| { + | _____________^ +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:91:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or.rs:70:13 | - = 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: aborting due to 13 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/map_unwrap_or_fixable.fixed b/src/tools/clippy/tests/ui/map_unwrap_or_fixable.fixed new file mode 100644 index 0000000000000..bd5b4f7165a4e --- /dev/null +++ b/src/tools/clippy/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 = 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/src/tools/clippy/tests/ui/map_unwrap_or_fixable.rs b/src/tools/clippy/tests/ui/map_unwrap_or_fixable.rs new file mode 100644 index 0000000000000..0b892caf20e82 --- /dev/null +++ b/src/tools/clippy/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 = 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/src/tools/clippy/tests/ui/map_unwrap_or_fixable.stderr b/src/tools/clippy/tests/ui/map_unwrap_or_fixable.stderr new file mode 100644 index 0000000000000..1837bc2ca3b8b --- /dev/null +++ b/src/tools/clippy/tests/ui/map_unwrap_or_fixable.stderr @@ -0,0 +1,22 @@ +error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` 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().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` 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/src/tools/clippy/tests/ui/match_type_on_diag_item.stderr b/src/tools/clippy/tests/ui/match_type_on_diag_item.stderr index 5e5fe9e3a3e73..82465dbaf6ecc 100644 --- a/src/tools/clippy/tests/ui/match_type_on_diag_item.stderr +++ b/src/tools/clippy/tests/ui/match_type_on_diag_item.stderr @@ -2,7 +2,7 @@ error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:41:17 | LL | let _ = match_type(cx, ty, &paths::VEC); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 @@ error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:42:17 | LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(option_type))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs index 80dd2f744b3a1..d93e5b114ecfa 100644 --- a/src/tools/clippy/tests/ui/methods.rs +++ b/src/tools/clippy/tests/ui/methods.rs @@ -122,16 +122,13 @@ impl Mul for T { 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/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr index 2a0a43e83a653..8a281c2dbd25c 100644 --- a/src/tools/clippy/tests/ui/methods.stderr +++ b/src/tools/clippy/tests/ui/methods.stderr @@ -8,27 +8,20 @@ LL | | } | = 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 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = 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 @@ LL | | ).is_some(); | |______________________________^ 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 @@ LL | | ).is_some(); | |______________________________^ 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 | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/methods_fixable.fixed b/src/tools/clippy/tests/ui/methods_fixable.fixed new file mode 100644 index 0000000000000..ee7c1b0da6d97 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/methods_fixable.rs b/src/tools/clippy/tests/ui/methods_fixable.rs new file mode 100644 index 0000000000000..6d0f1b7bd5147 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/methods_fixable.stderr b/src/tools/clippy/tests/ui/methods_fixable.stderr new file mode 100644 index 0000000000000..70e7c3dea545b --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed index a7fb00a270577..47e7460fa7a44 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.fixed +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs index 895fd86321faf..e2f8dec3b930c 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.rs +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/option_if_let_else.stderr b/src/tools/clippy/tests/ui/option_if_let_else.stderr index b69fe76768270..7aab068800a02 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.stderr +++ b/src/tools/clippy/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 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:16:12 + --> $DIR/option_if_let_else.rs:17:12 | LL | } else if let Some(x) = string { | ____________^ @@ -22,19 +22,19 @@ LL | | } | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs: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 @@ LL | }); | 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 @@ LL | }); | 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 @@ LL | }); | 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 @@ LL | }) | 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 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)` error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:70:13 + --> $DIR/option_if_let_else.rs:71:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -154,7 +154,7 @@ LL | }, |x| x * x * x * x); | 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/src/tools/clippy/tests/ui/option_map_or_none.stderr b/src/tools/clippy/tests/ui/option_map_or_none.stderr index 6f707987dbcaa..1cba29412b872 100644 --- a/src/tools/clippy/tests/ui/option_map_or_none.stderr +++ b/src/tools/clippy/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 @@ LL | let _ = opt.map_or(None, |x| Some(x + 1)); | = 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/src/tools/clippy/tests/ui/ref_option_ref.rs b/src/tools/clippy/tests/ui/ref_option_ref.rs new file mode 100644 index 0000000000000..b2c275d68afa1 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/ref_option_ref.stderr b/src/tools/clippy/tests/ui/ref_option_ref.stderr new file mode 100644 index 0000000000000..4e7fc80006118 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/single_char_add_str.fixed b/src/tools/clippy/tests/ui/single_char_add_str.fixed new file mode 100644 index 0000000000000..63a6d37a9cce9 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/single_char_add_str.rs b/src/tools/clippy/tests/ui/single_char_add_str.rs new file mode 100644 index 0000000000000..a799ea7d88564 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/single_char_add_str.stderr b/src/tools/clippy/tests/ui/single_char_add_str.stderr new file mode 100644 index 0000000000000..55d91583ad04d --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/single_char_pattern.fixed b/src/tools/clippy/tests/ui/single_char_pattern.fixed index 3871c4f2268cc..d8b5f19e144dc 100644 --- a/src/tools/clippy/tests/ui/single_char_pattern.fixed +++ b/src/tools/clippy/tests/ui/single_char_pattern.fixed @@ -12,15 +12,9 @@ fn main() { 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/src/tools/clippy/tests/ui/single_char_pattern.rs b/src/tools/clippy/tests/ui/single_char_pattern.rs index 32afe339cd81c..a7bc73e3756df 100644 --- a/src/tools/clippy/tests/ui/single_char_pattern.rs +++ b/src/tools/clippy/tests/ui/single_char_pattern.rs @@ -12,12 +12,6 @@ fn main() { 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/src/tools/clippy/tests/ui/single_char_pattern.stderr b/src/tools/clippy/tests/ui/single_char_pattern.stderr index fe7211c53f852..ee4e7e50efd17 100644 --- a/src/tools/clippy/tests/ui/single_char_pattern.stderr +++ b/src/tools/clippy/tests/ui/single_char_pattern.stderr @@ -7,160 +7,178 @@ LL | x.split("x"); = 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/src/tools/clippy/tests/ui/single_char_push_str.fixed b/src/tools/clippy/tests/ui/single_char_push_str.fixed deleted file mode 100644 index 0812c026a644f..0000000000000 --- a/src/tools/clippy/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/src/tools/clippy/tests/ui/single_char_push_str.rs b/src/tools/clippy/tests/ui/single_char_push_str.rs deleted file mode 100644 index ab293bbe4eeb5..0000000000000 --- a/src/tools/clippy/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/src/tools/clippy/tests/ui/single_char_push_str.stderr b/src/tools/clippy/tests/ui/single_char_push_str.stderr deleted file mode 100644 index 0e9bdaa23e7e8..0000000000000 --- a/src/tools/clippy/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/src/tools/clippy/tests/ui/skip_while_next.stderr b/src/tools/clippy/tests/ui/skip_while_next.stderr index a6b7bcd63ff39..269cc13468bc8 100644 --- a/src/tools/clippy/tests/ui/skip_while_next.stderr +++ b/src/tools/clippy/tests/ui/skip_while_next.stderr @@ -1,13 +1,13 @@ -error: called `skip_while(p).next()` on an `Iterator` +error: called `skip_while(

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

)` instead -error: called `skip_while(p).next()` on an `Iterator` +error: called `skip_while(

).next()` on an `Iterator` --> $DIR/skip_while_next.rs:17:13 | LL | let _ = v.iter().skip_while(|&x| { @@ -17,7 +17,7 @@ LL | | } LL | | ).next(); | |___________________________^ | - = help: this is more succinctly expressed by calling `.find(!p)` instead + = help: this is more succinctly expressed by calling `.find(!

)` instead error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed b/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed index 33605aca0199c..b129d95c5602f 100644 --- a/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed +++ b/src/tools/clippy/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 @@ fn main() { // 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/src/tools/clippy/tests/ui/toplevel_ref_arg.rs b/src/tools/clippy/tests/ui/toplevel_ref_arg.rs index 59759f1189333..73eb4ff7306f7 100644 --- a/src/tools/clippy/tests/ui/toplevel_ref_arg.rs +++ b/src/tools/clippy/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 @@ fn main() { // 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/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr b/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr index 19d69496709be..15cb933fedc9e 100644 --- a/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr +++ b/src/tools/clippy/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 @@ LL | let ref _x = 1; = 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/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs index 42cac2ba4de25..1a493fbce0ef3 100644 --- a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr index 295e2f35608bf..6c36141a58c65 100644 --- a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/unused_unit.fixed b/src/tools/clippy/tests/ui/unused_unit.fixed index 07f2791786d7f..7afc536135633 100644 --- a/src/tools/clippy/tests/ui/unused_unit.fixed +++ b/src/tools/clippy/tests/ui/unused_unit.fixed @@ -15,35 +15,35 @@ struct Unitter; impl Unitter { #[allow(clippy::no_effect)] - pub fn get_unit(&self, f: F, _g: G) - where G: Fn() { - let _y: &dyn Fn() = &f; + pub fn get_unit(&self, f: F, _g: G) + where G: Fn() { + let _y: &dyn Fn() = &f; (); // this should not lint, as it's not in return type position } } impl Into<()> for Unitter { #[rustfmt::skip] - fn into(self) { + fn into(self) { } } trait Trait { - fn redundant(&self, _f: F, _g: G, _h: H) + fn redundant(&self, _f: F, _g: G, _h: H) where - G: FnMut() , - H: Fn() ; + G: FnMut(), + H: Fn(); } impl Trait for Unitter { - fn redundant(&self, _f: F, _g: G, _h: H) + fn redundant(&self, _f: F, _g: G, _h: H) where - G: FnMut() , - H: Fn() {} + G: FnMut(), + H: Fn() {} } -fn return_unit() { } +fn return_unit() { } #[allow(clippy::needless_return)] #[allow(clippy::never_loop)] @@ -70,3 +70,12 @@ fn foo() { recv(rx) -> _x => () } } + +#[rustfmt::skip] +fn test(){} + +#[rustfmt::skip] +fn test2(){} + +#[rustfmt::skip] +fn test3(){} diff --git a/src/tools/clippy/tests/ui/unused_unit.rs b/src/tools/clippy/tests/ui/unused_unit.rs index e2c6afb020f58..96cef1ed5a5f2 100644 --- a/src/tools/clippy/tests/ui/unused_unit.rs +++ b/src/tools/clippy/tests/ui/unused_unit.rs @@ -70,3 +70,12 @@ fn foo() { recv(rx) -> _x => () } } + +#[rustfmt::skip] +fn test()->(){} + +#[rustfmt::skip] +fn test2() ->(){} + +#[rustfmt::skip] +fn test3()-> (){} diff --git a/src/tools/clippy/tests/ui/unused_unit.stderr b/src/tools/clippy/tests/ui/unused_unit.stderr index 81e6738e6bf67..c45634c2b6df7 100644 --- a/src/tools/clippy/tests/ui/unused_unit.stderr +++ b/src/tools/clippy/tests/ui/unused_unit.stderr @@ -1,8 +1,8 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:18:29 + --> $DIR/unused_unit.rs:18:28 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` | note: the lint level is defined here --> $DIR/unused_unit.rs:12:9 @@ -11,28 +11,28 @@ LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:19 + --> $DIR/unused_unit.rs:19:18 | LL | where G: Fn() -> () { - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:18:59 + --> $DIR/unused_unit.rs:18:58 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:20:27 + --> $DIR/unused_unit.rs:20:26 | LL | let _y: &dyn Fn() -> () = &f; - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:27:19 + --> $DIR/unused_unit.rs:27:18 | LL | fn into(self) -> () { - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression --> $DIR/unused_unit.rs:28:9 @@ -41,46 +41,46 @@ LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:33:30 + --> $DIR/unused_unit.rs:33:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:35:20 + --> $DIR/unused_unit.rs:35:19 | LL | G: FnMut() -> (), - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:36:17 + --> $DIR/unused_unit.rs:36:16 | LL | H: Fn() -> (); - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:40:30 + --> $DIR/unused_unit.rs:40:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:42:20 + --> $DIR/unused_unit.rs:42:19 | LL | G: FnMut() -> (), - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:43:17 + --> $DIR/unused_unit.rs:43:16 | LL | H: Fn() -> () {} - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:46:18 + --> $DIR/unused_unit.rs:46:17 | LL | fn return_unit() -> () { () } - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression --> $DIR/unused_unit.rs:46:26 @@ -100,5 +100,23 @@ error: unneeded `()` LL | return(); | ^^ help: remove the `()` -error: aborting due to 16 previous errors +error: unneeded unit return type + --> $DIR/unused_unit.rs:75:10 + | +LL | fn test()->(){} + | ^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:78:11 + | +LL | fn test2() ->(){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:81:11 + | +LL | fn test3()-> (){} + | ^^^^^ help: remove the `-> ()` + +error: aborting due to 19 previous errors