1
Fork 0

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

Update Clippy

Biweekly Clippy update

r? `@Manishearth`
This commit is contained in:
bors 2020-11-05 17:57:31 +00:00
commit 9d78d1d027
150 changed files with 2840 additions and 676 deletions

View file

@ -24,16 +24,16 @@ unset CARGO_MANIFEST_DIR
# FIXME: How to match the clippy invocation in compile-test.rs? # 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 ./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 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 # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same
SYSROOT=$(rustc --print sysroot) 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 echo "fn main() {}" >target/driver_test.rs
# we can't run 2 rustcs on the same file at the same time # 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) CLIPPY=$(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc)
RUSTC=$(rustc ./target/driver_test.rs) RUSTC=$(rustc ./target/driver_test.rs)
diff <($CLIPPY) <($RUSTC) diff -u <($CLIPPY) <($RUSTC)
# TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR # TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR

View file

@ -22,7 +22,7 @@ Current beta, release 2020-11-19
* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998) * [`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) * [`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) * [`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 ### 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 [`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 [`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_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 [`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 [`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 [`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 [`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 [`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 [`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 [`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`]: 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 [`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 [`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_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 [`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 [`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_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 [`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_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_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_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_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_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_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 [`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 [`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_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_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_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten [`map_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_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 [`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_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 [`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 [`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 [`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_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 [`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 [`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_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_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_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 [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match

View file

@ -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]. 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]. 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. [`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`]
They are mostly classified as [`E-medium`], since they might be somewhat involved code wise, first. Sometimes they are only somewhat involved code wise, but not difficult per-se.
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 [`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 lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of

View file

@ -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. * `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 If you do not want to include your lint levels in your code, you can globally
by passing extra flags to Clippy during the run: enable/disable lints by passing extra flags to Clippy during the run:
To disable `lint_name`, run To allow `lint_name`, run
```terminal ```terminal
cargo clippy -- -A clippy::lint_name cargo clippy -- -A clippy::lint_name
``` ```
And to enable `lint_name`, run And to warn on `lint_name`, run
```terminal ```terminal
cargo clippy -- -W clippy::lint_name cargo clippy -- -W clippy::lint_name
@ -190,7 +193,7 @@ can run Clippy with warnings for all lints enabled:
cargo clippy -- -W clippy::pedantic 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: the lint(s) you are interested in:
```terminal ```terminal
cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...

View file

@ -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 // 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>) { 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()); let rustc_path = PathBuf::from(rustc_path.unwrap());
assert!(rustc_path.is_dir(), "path is not a directory"); assert!(rustc_path.is_dir(), "path is not a directory");
let rustc_source_basedir = rustc_path.join("compiler"); let rustc_source_basedir = rustc_path.join("compiler");
@ -49,6 +49,15 @@ fn inject_deps_into_manifest(
cargo_toml: &str, cargo_toml: &str,
lib_rs: &str, lib_rs: &str,
) -> std::io::Result<()> { ) -> 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 let extern_crates = lib_rs
.lines() .lines()
// get the deps // get the deps

View file

@ -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)); 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() { if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
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"); span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
self.expr_span = Some(expr.span); self.expr_span = Some(expr.span);
} else if l_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { }
}
},
_ => {
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"); span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
self.expr_span = Some(expr.span); self.expr_span = Some(expr.span);
} }

View file

@ -16,6 +16,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::sym;
use rustc_span::symbol::{Symbol, SymbolStr}; use rustc_span::symbol::{Symbol, SymbolStr};
use semver::Version; 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; return;
} }
for item in items { for item in items {
if_chain! { if_chain! {
if let NestedMetaItem::MetaItem(mi) = &item; if let NestedMetaItem::MetaItem(mi) = &item;
if let MetaItemKind::NameValue(lit) = &mi.kind; if let MetaItemKind::NameValue(lit) = &mi.kind;
if mi.has_name(sym!(since)); if mi.has_name(sym::since);
then { then {
check_semver(cx, item.span(), lit); check_semver(cx, item.span(), lit);
} }
@ -309,7 +310,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
} }
match item.kind { match item.kind {
ItemKind::ExternCrate(..) | ItemKind::Use(..) => { 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 { for attr in item.attrs {
if in_external_macro(cx.sess(), attr.span) { if in_external_macro(cx.sess(), attr.span) {
@ -326,7 +327,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
match item.kind { match item.kind {
ItemKind::Use(..) => { ItemKind::Use(..) => {
if is_word(lint, sym!(unused_imports)) 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!(unreachable_pub))
|| is_word(lint, sym!(unused)) || is_word(lint, sym!(unused))
|| extract_clippy_lint(lint) || extract_clippy_lint(lint)
@ -411,8 +412,7 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMet
let lint_store = cx.lints(); let lint_store = cx.lints();
for lint in items { for lint in items {
if let Some(lint_name) = extract_clippy_lint(lint) { if let Some(lint_name) = extract_clippy_lint(lint) {
if let CheckLintNameResult::Tool(Err((None, _))) = if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(&lint_name, Some(sym::clippy))
lint_store.check_lint_name(&lint_name, Some(sym!(clippy)))
{ {
span_lint_and_then( span_lint_and_then(
cx, cx,
@ -529,10 +529,10 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut
for attr in attrs { for attr in attrs {
if let Some(values) = attr.meta_item_list() { 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; continue;
} }
if is_word(&values[0], sym!(always)) { if is_word(&values[0], sym::always) {
span_lint( span_lint(
cx, cx,
INLINE_ALWAYS, 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) { fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
if_chain! { if_chain! {
// check cfg_attr // 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 let Some(items) = attr.meta_item_list();
if items.len() == 2; if items.len() == 2;
// check for `rustfmt` // check for `rustfmt`
if let Some(feature_item) = items[0].meta_item(); 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` // check for `rustfmt_skip` and `rustfmt::skip`
if let Some(skip_item) = &items[1].meta_item(); if let Some(skip_item) = &items[1].meta_item();
if skip_item.has_name(sym!(rustfmt_skip)) || if skip_item.has_name(sym!(rustfmt_skip)) ||
@ -690,7 +690,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
} }
if_chain! { if_chain! {
if attr.has_name(sym!(cfg)); if attr.has_name(sym::cfg);
if let Some(list) = attr.meta_item_list(); if let Some(list) = attr.meta_item_list();
let mismatched = find_mismatched_target_os(&list); let mismatched = find_mismatched_target_os(&list);
if !mismatched.is_empty(); if !mismatched.is_empty();

View file

@ -11,6 +11,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for boolean expressions that can be written more /// **What it does:** Checks for boolean expressions that can be written more
@ -253,8 +254,8 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
}, },
ExprKind::MethodCall(path, _, args, _) if args.len() == 1 => { ExprKind::MethodCall(path, _, args, _) if args.len() == 1 => {
let type_of_receiver = cx.typeck_results().expr_ty(&args[0]); let type_of_receiver = cx.typeck_results().expr_ty(&args[0]);
if !is_type_diagnostic_item(cx, type_of_receiver, sym!(option_type)) if !is_type_diagnostic_item(cx, type_of_receiver, sym::option_type)
&& !is_type_diagnostic_item(cx, type_of_receiver, sym!(result_type)) && !is_type_diagnostic_item(cx, type_of_receiver, sym::result_type)
{ {
return None; return None;
} }

View file

@ -8,6 +8,7 @@ use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
use rustc_span::Symbol; use rustc_span::Symbol;
declare_clippy_lint! { declare_clippy_lint! {
@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) = let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) =
filter_args[0].kind { filter_args[0].kind {
let p = path.ident.name; 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] &args[0]
} else { } else {
&filter_args[0] &filter_args[0]

View file

@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span; 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}; use crate::utils::{is_type_diagnostic_item, snippet_opt, span_lint_and_help, LimitStack};
@ -61,7 +61,7 @@ impl CognitiveComplexity {
helper.visit_expr(expr); helper.visit_expr(expr);
let CCHelper { cc, returns } = helper; let CCHelper { cc, returns } = helper;
let ret_ty = cx.typeck_results().node_type(expr.hir_id); 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 returns
} else { } else {
#[allow(clippy::integer_division)] #[allow(clippy::integer_division)]
@ -123,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity {
hir_id: HirId, hir_id: HirId,
) { ) {
let def_id = cx.tcx.hir().local_def_id(hir_id); 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); self.check(cx, kind, decl, body, span);
} }
} }

View file

@ -7,10 +7,10 @@ use rustc_data_structures::sync::Lrc;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp}; use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty::subst::{Subst, SubstsRef};
use rustc_middle::ty::{self, Ty, TyCtxt, ScalarInt};
use rustc_middle::{bug, span_bug};
use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::interpret::Scalar;
use rustc_middle::ty::subst::{Subst, SubstsRef};
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use std::cmp::Ordering::{self, Equal}; use std::cmp::Ordering::{self, Equal};
use std::convert::TryInto; use std::convert::TryInto;
@ -501,7 +501,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
} }
pub fn miri_to_const(result: &ty::Const<'_>) -> Option<Constant> { pub fn miri_to_const(result: &ty::Const<'_>) -> Option<Constant> {
use rustc_middle::mir::interpret::{ConstValue}; use rustc_middle::mir::interpret::ConstValue;
match result.val { match result.val {
ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => { ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => {
match result.ty.kind() { match result.ty.kind() {

View file

@ -0,0 +1,304 @@
use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet};
use crate::utils::{span_lint_and_note, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Adt, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::Span;
declare_clippy_lint! {
/// **What it does:** Checks for literal calls to `Default::default()`.
///
/// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is
/// being gotten than the generic `Default`.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// // Bad
/// let s: String = Default::default();
///
/// // Good
/// let s = String::default();
/// ```
pub DEFAULT_TRAIT_ACCESS,
pedantic,
"checks for literal calls to `Default::default()`"
}
declare_clippy_lint! {
/// **What it does:** Checks for immediate reassignment of fields initialized
/// with Default::default().
///
/// **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).
///
/// **Known problems:** Assignments to patterns that are of tuple type are not linted.
///
/// **Example:**
/// Bad:
/// ```
/// # #[derive(Default)]
/// # struct A { i: i32 }
/// let mut a: A = Default::default();
/// a.i = 42;
/// ```
/// Use instead:
/// ```
/// # #[derive(Default)]
/// # struct A { i: i32 }
/// let a = A {
/// i: 42,
/// .. Default::default()
/// };
/// ```
pub FIELD_REASSIGN_WITH_DEFAULT,
style,
"binding initialized with Default should have its fields set in the initializer"
}
#[derive(Default)]
pub struct Default {
// Spans linted by `field_reassign_with_default`.
reassigned_linted: FxHashSet<Span>,
}
impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
impl LateLintPass<'_> for Default {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
// Avoid cases already linted by `field_reassign_with_default`
if !self.reassigned_linted.contains(&expr.span);
if let ExprKind::Call(ref path, ..) = expr.kind;
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
if let ExprKind::Path(ref qpath) = path.kind;
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
// Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
if let QPath::Resolved(None, _path) = qpath;
then {
let expr_ty = cx.typeck_results().expr_ty(expr);
if let ty::Adt(def, ..) = expr_ty.kind() {
// TODO: Work out a way to put "whatever the imported way of referencing
// this type in this file" rather than a fully-qualified type.
let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
span_lint_and_sugg(
cx,
DEFAULT_TRAIT_ACCESS,
expr.span,
&format!("calling `{}` is more clear than this expression", replacement),
"try",
replacement,
Applicability::Unspecified, // First resolve the TODO above
);
}
}
}
}
fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the
// `default` method of the `Default` trait, and store statement index in current block being
// checked and the name of the bound variable
let binding_statements_using_default = enumerate_bindings_using_default(cx, block);
// start from the `let mut _ = _::default();` and look at all the following
// statements, see if they re-assign the fields of the binding
for (stmt_idx, binding_name, binding_type, span) in binding_statements_using_default {
// the last statement of a block cannot trigger the lint
if stmt_idx == block.stmts.len() - 1 {
break;
}
// find all "later statement"'s where the fields of the binding set as
// Default::default() get reassigned, unless the reassignment refers to the original binding
let mut first_assign = None;
let mut assigned_fields = Vec::new();
let mut cancel_lint = false;
for consecutive_statement in &block.stmts[stmt_idx + 1..] {
// interrupt if the statement is a let binding (`Local`) that shadows the original
// binding
if stmt_shadows_binding(consecutive_statement, binding_name) {
break;
}
// find out if and which field was set by this `consecutive_statement`
else if let Some((field_ident, assign_rhs)) =
field_reassigned_by_stmt(consecutive_statement, binding_name)
{
// interrupt and cancel lint if assign_rhs references the original binding
if contains_name(binding_name, assign_rhs) {
cancel_lint = true;
break;
}
// if the field was previously assigned, replace the assignment, otherwise insert the assignment
if let Some(prev) = assigned_fields
.iter_mut()
.find(|(field_name, _)| field_name == &field_ident.name)
{
*prev = (field_ident.name, assign_rhs);
} else {
assigned_fields.push((field_ident.name, assign_rhs));
}
// also set first instance of error for help message
if first_assign.is_none() {
first_assign = Some(consecutive_statement);
}
}
// interrupt also if no field was assigned, since we only want to look at consecutive statements
else {
break;
}
}
// if there are incorrectly assigned fields, do a span_lint_and_note to suggest
// construction using `Ty { fields, ..Default::default() }`
if !assigned_fields.is_empty() && !cancel_lint {
// take the original assignment as span
let stmt = &block.stmts[stmt_idx];
if let StmtKind::Local(preceding_local) = &stmt.kind {
// filter out fields like `= Default::default()`, because the FRU already covers them
let assigned_fields = assigned_fields
.into_iter()
.filter(|(_, rhs)| !is_expr_default(rhs, cx))
.collect::<Vec<(Symbol, &Expr<'_>)>>();
// if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
let ext_with_default = !fields_of_type(binding_type)
.iter()
.all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name));
let field_list = assigned_fields
.into_iter()
.map(|(field, rhs)| {
// extract and store the assigned value for help message
let value_snippet = snippet(cx, rhs.span, "..");
format!("{}: {}", field, value_snippet)
})
.collect::<Vec<String>>()
.join(", ");
let sugg = if ext_with_default {
if field_list.is_empty() {
format!("{}::default()", binding_type)
} else {
format!("{} {{ {}, ..Default::default() }}", binding_type, field_list)
}
} else {
format!("{} {{ {} }}", binding_type, field_list)
};
// span lint once per statement that binds default
span_lint_and_note(
cx,
FIELD_REASSIGN_WITH_DEFAULT,
first_assign.unwrap().span,
"field assignment outside of initializer for an instance created with Default::default()",
Some(preceding_local.span),
&format!(
"consider initializing the variable with `{}` and removing relevant reassignments",
sugg
),
);
self.reassigned_linted.insert(span);
}
}
}
}
}
/// Checks if the given expression is the `default` method belonging to the `Default` trait.
fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool {
if_chain! {
if let ExprKind::Call(ref fn_expr, _) = &expr.kind;
if let ExprKind::Path(qpath) = &fn_expr.kind;
if let Res::Def(_, def_id) = qpath_res(cx, qpath, fn_expr.hir_id);
then {
// right hand side of assignment is `Default::default`
match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD)
} else {
false
}
}
}
/// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except
/// for when the pattern type is a tuple.
fn enumerate_bindings_using_default<'tcx>(
cx: &LateContext<'tcx>,
block: &Block<'tcx>,
) -> Vec<(usize, Symbol, Ty<'tcx>, Span)> {
block
.stmts
.iter()
.enumerate()
.filter_map(|(idx, stmt)| {
if_chain! {
// only take `let ...` statements
if let StmtKind::Local(ref local) = stmt.kind;
// only take bindings to identifiers
if let PatKind::Binding(_, _, ident, _) = local.pat.kind;
// that are not tuples
let ty = cx.typeck_results().pat_ty(local.pat);
if !matches!(ty.kind(), ty::Tuple(_));
// only when assigning `... = Default::default()`
if let Some(ref expr) = local.init;
if is_expr_default(expr, cx);
then {
Some((idx, ident.name, ty, expr.span))
} else {
None
}
}
})
.collect()
}
fn stmt_shadows_binding(this: &Stmt<'_>, shadowed: Symbol) -> bool {
if let StmtKind::Local(local) = &this.kind {
if let PatKind::Binding(_, _, ident, _) = local.pat.kind {
return ident.name == shadowed;
}
}
false
}
/// Returns the reassigned field and the assigning expression (right-hand side of assign).
fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
if_chain! {
// only take assignments
if let StmtKind::Semi(ref later_expr) = this.kind;
if let ExprKind::Assign(ref assign_lhs, ref assign_rhs, _) = later_expr.kind;
// only take assignments to fields where the left-hand side field is a field of
// the same binding as the previous statement
if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind;
if let ExprKind::Path(ref qpath) = binding.kind;
if let QPath::Resolved(_, path) = qpath;
if let Some(second_binding_name) = path.segments.last();
if second_binding_name.ident.name == binding_name;
then {
Some((field_ident, assign_rhs))
} else {
None
}
}
}
/// Returns the vec of fields for a struct and an empty vec for non-struct ADTs.
fn fields_of_type(ty: Ty<'_>) -> Vec<Ident> {
if let Adt(adt, _) = ty.kind() {
if adt.is_struct() {
let variant = &adt.non_enum_variant();
return variant.fields.iter().map(|f| f.ident).collect();
}
}
vec![]
}

View file

@ -1,62 +0,0 @@
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use crate::utils::{any_parent_is_automatically_derived, match_def_path, paths, span_lint_and_sugg};
declare_clippy_lint! {
/// **What it does:** Checks for literal calls to `Default::default()`.
///
/// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is
/// being gotten than the generic `Default`.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// // Bad
/// let s: String = Default::default();
///
/// // Good
/// let s = String::default();
/// ```
pub DEFAULT_TRAIT_ACCESS,
pedantic,
"checks for literal calls to `Default::default()`"
}
declare_lint_pass!(DefaultTraitAccess => [DEFAULT_TRAIT_ACCESS]);
impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
if let ExprKind::Call(ref path, ..) = expr.kind;
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
if let ExprKind::Path(ref qpath) = path.kind;
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
// Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
if let QPath::Resolved(None, _path) = qpath;
then {
let expr_ty = cx.typeck_results().expr_ty(expr);
if let ty::Adt(def, ..) = expr_ty.kind() {
// TODO: Work out a way to put "whatever the imported way of referencing
// this type in this file" rather than a fully-qualified type.
let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
span_lint_and_sugg(
cx,
DEFAULT_TRAIT_ACCESS,
expr.span,
&format!("calling `{}` is more clear than this expression", replacement),
"try",
replacement,
Applicability::Unspecified, // First resolve the TODO above
);
}
}
}
}
}

View file

@ -15,7 +15,7 @@ use rustc_parse::maybe_new_parser_from_source_str;
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; 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::io;
use std::ops::Range; use std::ops::Range;
use url::Url; use url::Url;
@ -237,7 +237,7 @@ fn lint_for_missing_headers<'tcx>(
); );
} }
if !headers.errors { 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( span_lint(
cx, cx,
MISSING_ERRORS_DOC, MISSING_ERRORS_DOC,
@ -255,7 +255,7 @@ fn lint_for_missing_headers<'tcx>(
if let ty::Opaque(_, subs) = ret_ty.kind(); if let ty::Opaque(_, subs) = ret_ty.kind();
if let Some(gen) = subs.types().next(); if let Some(gen) = subs.types().next();
if let ty::Generator(_, subs, _) = gen.kind(); 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 { then {
span_lint( span_lint(
cx, cx,
@ -333,7 +333,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span); let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span);
spans.extend_from_slice(&current_spans); spans.extend_from_slice(&current_spans);
doc.push_str(&comment); 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 // ignore mix of sugared and non-sugared doc
// don't trigger the safety or errors check // don't trigger the safety or errors check
return DocHeaders { return DocHeaders {
@ -479,7 +479,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, span: Span) {
| ItemKind::ExternCrate(..) | ItemKind::ExternCrate(..)
| ItemKind::ForeignMod(..) => return false, | ItemKind::ForeignMod(..) => return false,
// We found a main function ... // 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 is_async = matches!(sig.header.asyncness, Async::Yes{..});
let returns_nothing = match &sig.decl.output { let returns_nothing = match &sig.decl.output {
FnRetTy::Default(..) => true, FnRetTy::Default(..) => true,

View file

@ -5,6 +5,7 @@ use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be /// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be
@ -33,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
if_chain! { if_chain! {
// match call to unwrap // match call to unwrap
if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args, _) = expr.kind; 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 // match call to write_fmt
if !unwrap_args.is_empty(); if !unwrap_args.is_empty();
if let ExprKind::MethodCall(ref write_fun, _, write_args, _) = if let ExprKind::MethodCall(ref write_fun, _, write_args, _) =

View file

@ -6,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span; use rustc_span::{sym, Span};
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()` /// **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` // check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); 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)) if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type)
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) || is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type)
{ {
self.result.push(expr.span); 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 { for impl_item in impl_items {
if_chain! { if_chain! {
if impl_item.ident.name == sym!(from); if impl_item.ident.name == sym::from;
if let ImplItemKind::Fn(_, body_id) = if let ImplItemKind::Fn(_, body_id) =
cx.tcx.hir().impl_item(impl_item.id).kind; cx.tcx.hir().impl_item(impl_item.id).kind;
then { then {

View file

@ -10,6 +10,7 @@ use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, MatchSource, PatKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for the use of `format!("string literal with no /// **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; if pats.len() == 1;
then { then {
let ty = cx.typeck_results().pat_ty(&pats[0]).peel_refs(); 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; return None;
} }
if let ExprKind::Lit(ref lit) = format_args.kind { if let ExprKind::Lit(ref lit) = format_args.kind {
@ -186,15 +187,15 @@ fn check_unformatted(expr: &Expr<'_>) -> bool {
if exprs.len() == 1; if exprs.len() == 1;
// struct `core::fmt::rt::v1::Argument` // struct `core::fmt::rt::v1::Argument`
if let ExprKind::Struct(_, ref fields, _) = exprs[0].kind; 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` // struct `core::fmt::rt::v1::FormatSpec`
if let ExprKind::Struct(_, ref fields, _) = format_field.expr.kind; 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 let ExprKind::Path(ref precision_path) = precision_field.expr.kind;
if last_path_segment(precision_path).ident.name == sym!(Implied); 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 Some(width_field) = fields.iter().find(|f| f.ident.name == sym::width);
if let ExprKind::Path(ref width_qpath) = width_field.expr.kind; 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 { then {
return true; return true;
} }

View file

@ -16,6 +16,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::sym;
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
use rustc_typeck::hir_ty_to_ty; 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 !in_external_macro(cx.sess(), item_span);
if let hir::FnRetTy::Return(ref ty) = decl.output; if let hir::FnRetTy::Return(ref ty) = decl.output;
if let hir::TyKind::Path(ref qpath) = ty.kind; 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 Some(ref args) = last_path_segment(qpath).args;
if let [_, hir::GenericArg::Type(ref err_ty)] = args.args; if let [_, hir::GenericArg::Type(ref err_ty)] = args.args;
if let hir::TyKind::Tup(t) = err_ty.kind; if let hir::TyKind::Tup(t) = err_ty.kind;

View file

@ -8,6 +8,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for using `x.get(x.len() - 1)` instead of /// **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 // Argument 0 (the struct we're calling the method on) is a vector
if let Some(struct_calling_on) = args.get(0); if let Some(struct_calling_on) = args.get(0);
let struct_ty = cx.typeck_results().expr_ty(struct_calling_on); 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 // Argument to "get" is a subtraction
if let Some(get_index_arg) = args.get(1); if let Some(get_index_arg) = args.get(1);

View file

@ -4,6 +4,7 @@ use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath}; use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:*** Checks for unnecessary `ok()` in if let. /// **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<T,E>.ok(, _) if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = op.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation if 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 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"; if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
then { then {

View file

@ -2,6 +2,7 @@ use if_chain::if_chain;
use rustc_hir::{ImplItem, ImplItemKind}; use rustc_hir::{ImplItem, ImplItemKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
use crate::utils::{ use crate::utils::{
get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help, 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; if decl.inputs.len() == 1;
// Check if return type is String // 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 // Filters instances of to_string which are required by a trait
if trait_ref_of_method(cx, impl_item.hir_id).is_none(); if trait_ref_of_method(cx, impl_item.hir_id).is_none();

View file

@ -7,7 +7,7 @@ use rustc_errors::Applicability;
use rustc_hir::{TraitFn, TraitItem, TraitItemKind}; use rustc_hir::{TraitFn, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Symbol; use rustc_span::{sym, Symbol};
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for `#[inline]` on trait methods without bodies /// **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]) { fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) {
for attr in attrs { for attr in attrs {
if !attr.has_name(sym!(inline)) { if !attr.has_name(sym::inline) {
continue; continue;
} }

View file

@ -2,7 +2,8 @@
use crate::utils::span_lint; use crate::utils::span_lint;
use rustc_ast::ast::{Block, ItemKind, StmtKind}; 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}; use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! { declare_clippy_lint! {
@ -53,7 +54,7 @@ declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]);
impl EarlyLintPass for ItemsAfterStatements { impl EarlyLintPass for ItemsAfterStatements {
fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) { fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) {
if item.span.from_expansion() { if in_external_macro(cx.sess(), item.span) {
return; return;
} }
@ -67,7 +68,7 @@ impl EarlyLintPass for ItemsAfterStatements {
// lint on all further items // lint on all further items
for stmt in stmts { for stmt in stmts {
if let StmtKind::Item(ref it) = *stmt { if let StmtKind::Item(ref it) = *stmt {
if it.span.from_expansion() { if in_external_macro(cx.sess(), it.span) {
return; return;
} }
if let ItemKind::MacroDef(..) = it.kind { if let ItemKind::MacroDef(..) = it.kind {

View file

@ -68,7 +68,44 @@ declare_clippy_lint! {
"traits or impls with a public `len` method but no corresponding `is_empty` method" "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 { impl<'tcx> LateLintPass<'tcx> for LenZero {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { 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) 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. /// Checks if this type has an `is_empty` method.
fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
/// Gets an `AssocItem` and return true if it matches `is_empty(self)`. /// Gets an `AssocItem` and return true if it matches `is_empty(self)`.

View file

@ -176,7 +176,7 @@ mod copies;
mod copy_iterator; mod copy_iterator;
mod create_dir; mod create_dir;
mod dbg_macro; mod dbg_macro;
mod default_trait_access; mod default;
mod dereference; mod dereference;
mod derive; mod derive;
mod disallowed_method; mod disallowed_method;
@ -234,6 +234,7 @@ mod macro_use;
mod main_recursion; mod main_recursion;
mod manual_async_fn; mod manual_async_fn;
mod manual_non_exhaustive; mod manual_non_exhaustive;
mod manual_ok_or;
mod manual_strip; mod manual_strip;
mod manual_unwrap_or; mod manual_unwrap_or;
mod map_clone; mod map_clone;
@ -294,6 +295,7 @@ mod redundant_closure_call;
mod redundant_field_names; mod redundant_field_names;
mod redundant_pub_crate; mod redundant_pub_crate;
mod redundant_static_lifetimes; mod redundant_static_lifetimes;
mod ref_option_ref;
mod reference; mod reference;
mod regex; mod regex;
mod repeat_once; mod repeat_once;
@ -537,7 +539,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&copy_iterator::COPY_ITERATOR, &copy_iterator::COPY_ITERATOR,
&create_dir::CREATE_DIR, &create_dir::CREATE_DIR,
&dbg_macro::DBG_MACRO, &dbg_macro::DBG_MACRO,
&default_trait_access::DEFAULT_TRAIT_ACCESS, &default::DEFAULT_TRAIT_ACCESS,
&default::FIELD_REASSIGN_WITH_DEFAULT,
&dereference::EXPLICIT_DEREF_METHODS, &dereference::EXPLICIT_DEREF_METHODS,
&derive::DERIVE_HASH_XOR_EQ, &derive::DERIVE_HASH_XOR_EQ,
&derive::DERIVE_ORD_XOR_PARTIAL_ORD, &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_const_arrays::LARGE_CONST_ARRAYS,
&large_enum_variant::LARGE_ENUM_VARIANT, &large_enum_variant::LARGE_ENUM_VARIANT,
&large_stack_arrays::LARGE_STACK_ARRAYS, &large_stack_arrays::LARGE_STACK_ARRAYS,
&len_zero::COMPARISON_TO_EMPTY,
&len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_WITHOUT_IS_EMPTY,
&len_zero::LEN_ZERO, &len_zero::LEN_ZERO,
&let_if_seq::USELESS_LET_IF_SEQ, &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, &main_recursion::MAIN_RECURSION,
&manual_async_fn::MANUAL_ASYNC_FN, &manual_async_fn::MANUAL_ASYNC_FN,
&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
&manual_ok_or::MANUAL_OK_OR,
&manual_strip::MANUAL_STRIP, &manual_strip::MANUAL_STRIP,
&manual_unwrap_or::MANUAL_UNWRAP_OR, &manual_unwrap_or::MANUAL_UNWRAP_OR,
&map_clone::MAP_CLONE, &map_clone::MAP_CLONE,
@ -692,6 +697,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&methods::FILTER_NEXT, &methods::FILTER_NEXT,
&methods::FIND_MAP, &methods::FIND_MAP,
&methods::FLAT_MAP_IDENTITY, &methods::FLAT_MAP_IDENTITY,
&methods::FROM_ITER_INSTEAD_OF_COLLECT,
&methods::GET_UNWRAP, &methods::GET_UNWRAP,
&methods::INEFFICIENT_TO_STRING, &methods::INEFFICIENT_TO_STRING,
&methods::INTO_ITER_ON_REF, &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_NTH_ZERO,
&methods::ITER_SKIP_NEXT, &methods::ITER_SKIP_NEXT,
&methods::MANUAL_SATURATING_ARITHMETIC, &methods::MANUAL_SATURATING_ARITHMETIC,
&methods::MAP_COLLECT_RESULT_UNIT,
&methods::MAP_FLATTEN, &methods::MAP_FLATTEN,
&methods::MAP_UNWRAP_OR, &methods::MAP_UNWRAP_OR,
&methods::NEW_RET_NO_SELF, &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::RESULT_MAP_OR_INTO_OPTION,
&methods::SEARCH_IS_SOME, &methods::SEARCH_IS_SOME,
&methods::SHOULD_IMPLEMENT_TRAIT, &methods::SHOULD_IMPLEMENT_TRAIT,
&methods::SINGLE_CHAR_ADD_STR,
&methods::SINGLE_CHAR_PATTERN, &methods::SINGLE_CHAR_PATTERN,
&methods::SINGLE_CHAR_PUSH_STR,
&methods::SKIP_WHILE_NEXT, &methods::SKIP_WHILE_NEXT,
&methods::STRING_EXTEND_CHARS, &methods::STRING_EXTEND_CHARS,
&methods::SUSPICIOUS_MAP, &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_field_names::REDUNDANT_FIELD_NAMES,
&redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_pub_crate::REDUNDANT_PUB_CRATE,
&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
&ref_option_ref::REF_OPTION_REF,
&reference::DEREF_ADDROF, &reference::DEREF_ADDROF,
&reference::REF_IN_DEREF, &reference::REF_IN_DEREF,
&regex::INVALID_REGEX, &regex::INVALID_REGEX,
@ -1030,6 +1038,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&sess.target, &sess.target,
); );
store.register_late_pass(move || box pass_by_ref_or_value); 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 try_err::TryErr);
store.register_late_pass(|| box use_self::UseSelf); store.register_late_pass(|| box use_self::UseSelf);
store.register_late_pass(|| box bytecount::ByteCount); 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 neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd);
store.register_late_pass(|| box unwrap::Unwrap); store.register_late_pass(|| box unwrap::Unwrap);
store.register_late_pass(|| box duration_subsec::DurationSubsec); 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 indexing_slicing::IndexingSlicing);
store.register_late_pass(|| box non_copy_const::NonCopyConst); store.register_late_pass(|| box non_copy_const::NonCopyConst);
store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); 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; 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(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); 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 unused_self::UnusedSelf);
store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
store.register_late_pass(|| box exit::Exit); 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 unwrap_in_result::UnwrapInResult);
store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box self_assignment::SelfAssignment);
store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); 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 float_equality_without_abs::FloatEqualityWithoutAbs);
store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
store.register_late_pass(|| box manual_strip::ManualStrip); 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(&checked_conversions::CHECKED_CONVERSIONS),
LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
LintId::of(&copy_iterator::COPY_ITERATOR), LintId::of(&copy_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(&dereference::EXPLICIT_DEREF_METHODS),
LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY), LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY),
LintId::of(&derive::UNSAFE_DERIVE_DESERIALIZE), 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_INTO_ITER_LOOP),
LintId::of(&loops::EXPLICIT_ITER_LOOP), LintId::of(&loops::EXPLICIT_ITER_LOOP),
LintId::of(&macro_use::MACRO_USE_IMPORTS), LintId::of(&macro_use::MACRO_USE_IMPORTS),
LintId::of(&manual_ok_or::MANUAL_OK_OR),
LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&map_err_ignore::MAP_ERR_IGNORE),
LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
LintId::of(&matches::MATCH_BOOL), 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(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_MINUS_ONE),
LintId::of(&ranges::RANGE_PLUS_ONE), LintId::of(&ranges::RANGE_PLUS_ONE),
LintId::of(&ref_option_ref::REF_OPTION_REF),
LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&shadow::SHADOW_UNRELATED),
LintId::of(&strings::STRING_ADD_ASSIGN), LintId::of(&strings::STRING_ADD_ASSIGN),
LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), 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(&comparison_chain::COMPARISON_CHAIN),
LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IFS_SAME_COND),
LintId::of(&copies::IF_SAME_THEN_ELSE), 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_HASH_XOR_EQ),
LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD),
LintId::of(&doc::MISSING_SAFETY_DOC), 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(&int_plus_one::INT_PLUS_ONE),
LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS), LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS),
LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), 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_WITHOUT_IS_EMPTY),
LintId::of(&len_zero::LEN_ZERO), LintId::of(&len_zero::LEN_ZERO),
LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), 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::EXPECT_FUN_CALL),
LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FILTER_NEXT),
LintId::of(&methods::FLAT_MAP_IDENTITY), LintId::of(&methods::FLAT_MAP_IDENTITY),
LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::INTO_ITER_ON_REF),
LintId::of(&methods::ITERATOR_STEP_BY_ZERO), LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
LintId::of(&methods::ITER_CLONED_COLLECT), 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_NTH_ZERO),
LintId::of(&methods::ITER_SKIP_NEXT), LintId::of(&methods::ITER_SKIP_NEXT),
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(&methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(&methods::NEW_RET_NO_SELF), LintId::of(&methods::NEW_RET_NO_SELF),
LintId::of(&methods::OK_EXPECT), LintId::of(&methods::OK_EXPECT),
LintId::of(&methods::OPTION_AS_REF_DEREF), 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::RESULT_MAP_OR_INTO_OPTION),
LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SEARCH_IS_SOME),
LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT),
LintId::of(&methods::SINGLE_CHAR_ADD_STR),
LintId::of(&methods::SINGLE_CHAR_PATTERN), LintId::of(&methods::SINGLE_CHAR_PATTERN),
LintId::of(&methods::SINGLE_CHAR_PUSH_STR),
LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::SKIP_WHILE_NEXT),
LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::STRING_EXTEND_CHARS),
LintId::of(&methods::SUSPICIOUS_MAP), 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(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&collapsible_if::COLLAPSIBLE_IF),
LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&comparison_chain::COMPARISON_CHAIN),
LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT),
LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::MISSING_SAFETY_DOC),
LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),
LintId::of(&enum_variants::ENUM_VARIANT_NAMES), 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(&functions::RESULT_UNIT_ERR),
LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
LintId::of(&inherent_to_string::INHERENT_TO_STRING), 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_WITHOUT_IS_EMPTY),
LintId::of(&len_zero::LEN_ZERO), LintId::of(&len_zero::LEN_ZERO),
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), 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(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
LintId::of(&methods::CHARS_LAST_CMP), LintId::of(&methods::CHARS_LAST_CMP),
LintId::of(&methods::CHARS_NEXT_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::INTO_ITER_ON_REF),
LintId::of(&methods::ITER_CLONED_COLLECT), LintId::of(&methods::ITER_CLONED_COLLECT),
LintId::of(&methods::ITER_NEXT_SLICE), LintId::of(&methods::ITER_NEXT_SLICE),
LintId::of(&methods::ITER_NTH_ZERO), LintId::of(&methods::ITER_NTH_ZERO),
LintId::of(&methods::ITER_SKIP_NEXT), LintId::of(&methods::ITER_SKIP_NEXT),
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(&methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(&methods::NEW_RET_NO_SELF), LintId::of(&methods::NEW_RET_NO_SELF),
LintId::of(&methods::OK_EXPECT), LintId::of(&methods::OK_EXPECT),
LintId::of(&methods::OPTION_MAP_OR_NONE), LintId::of(&methods::OPTION_MAP_OR_NONE),
LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION), LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION),
LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), 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::STRING_EXTEND_CHARS),
LintId::of(&methods::UNNECESSARY_FOLD), LintId::of(&methods::UNNECESSARY_FOLD),
LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), 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::for_loop_over_result", "clippy::for_loops_over_fallibles");
ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); 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. // only exists to let the dogfood integration test works.

View file

@ -16,7 +16,6 @@ use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, Symbol}; use rustc_span::symbol::{kw, Symbol};
use std::iter::FromIterator;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for lifetime annotations which can be removed by /// **What it does:** Checks for lifetime annotations which can be removed by
@ -214,14 +213,15 @@ fn could_use_elision<'tcx>(
} }
if allowed_lts if allowed_lts
.intersection(&FxHashSet::from_iter( .intersection(
input_visitor &input_visitor
.nested_elision_site_lts .nested_elision_site_lts
.iter() .iter()
.chain(output_visitor.nested_elision_site_lts.iter()) .chain(output_visitor.nested_elision_site_lts.iter())
.cloned() .cloned()
.filter(|v| matches!(v, RefLt::Named(_))), .filter(|v| matches!(v, RefLt::Named(_)))
)) .collect(),
)
.next() .next()
.is_some() .is_some()
{ {

View file

@ -619,9 +619,9 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
} }
let lhs_constructor = last_path_segment(qpath); 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) && match_trait_method(cx, match_expr, &paths::ITERATOR)
&& lhs_constructor.ident.name == sym!(Some) && lhs_constructor.ident.name == sym::Some
&& (pat_args.is_empty() && (pat_args.is_empty()
|| !is_refutable(cx, &pat_args[0]) || !is_refutable(cx, &pat_args[0])
&& !is_used_inside(cx, iter_expr, &arms[0].body) && !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, _ => 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> { fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
if_chain! { if_chain! {
if let ExprKind::MethodCall(method, _, args, _) = expr.kind; 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 args.len() == 1;
if let Some(arg) = args.get(0); if let Some(arg) = args.get(0);
then { arg } else { expr } 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(self_expr) = args.get(0);
if let Some(pushed_item) = args.get(1); if let Some(pushed_item) = args.get(1);
// Check that the method being called is push() on a Vec // 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"; if path.ident.name.as_str() == "push";
then { then {
return Some((self_expr, pushed_item)) 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. /// Checks for `for` loops over `Option`s and `Result`s.
fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
let ty = cx.typeck_results().expr_ty(arg); 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( span_lint_and_help(
cx, cx,
FOR_LOOPS_OVER_FALLIBLES, FOR_LOOPS_OVER_FALLIBLES,
@ -1753,7 +1753,7 @@ fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
snippet(cx, arg.span, "_") 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( span_lint_and_help(
cx, cx,
FOR_LOOPS_OVER_FALLIBLES, FOR_LOOPS_OVER_FALLIBLES,
@ -2186,8 +2186,8 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
if_chain! { if_chain! {
// a range index op // a range index op
if let ExprKind::MethodCall(ref meth, _, ref args, _) = expr.kind; if let ExprKind::MethodCall(ref meth, _, ref args, _) = expr.kind;
if (meth.ident.name == sym!(index) && match_trait_method(self.cx, expr, &paths::INDEX)) 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)); || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
if !self.check(&args[1], &args[0], expr); if !self.check(&args[1], &args[0], expr);
then { return } then { return }
} }
@ -2333,7 +2333,7 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
// will allow further borrows afterwards // will allow further borrows afterwards
let ty = cx.typeck_results().expr_ty(e); let ty = cx.typeck_results().expr_ty(e);
is_iterable_array(ty, cx) || 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) || match_type(cx, ty, &paths::LINKED_LIST) ||
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) ||
is_type_diagnostic_item(cx, ty, sym!(hashset_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); if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
then { then {
let ty = cx.typeck_results().node_type(ty.hir_id); 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)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
match_type(cx, ty, &paths::BTREEMAP) || match_type(cx, ty, &paths::BTREEMAP) ||
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) { is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) {

View file

@ -5,7 +5,7 @@ use rustc_attr as attr;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span; use rustc_span::{sym, Span};
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for manual implementations of the non-exhaustive pattern. /// **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 { fn is_doc_hidden(attr: &Attribute) -> bool {
attr.has_name(sym!(doc)) attr.has_name(sym::doc)
&& match attr.meta_item_list() { && 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, 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", "this seems like a manual implementation of the non-exhaustive pattern",
|diag| { |diag| {
if_chain! { 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, '{'); let header_span = cx.sess.source_map().span_until_char(item.span, '{');
if let Some(snippet) = snippet_opt(cx, header_span); if let Some(snippet) = snippet_opt(cx, header_span);
then { 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", "this seems like a manual implementation of the non-exhaustive pattern",
|diag| { |diag| {
if_chain! { 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); let header_span = find_header_span(cx, item, data);
if let Some(snippet) = snippet_opt(cx, header_span); if let Some(snippet) = snippet_opt(cx, header_span);
then { then {

View file

@ -0,0 +1,99 @@
use crate::utils::{
indent_of, is_type_diagnostic_item, match_qpath, paths, reindent_multiline, snippet_opt, span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{def, Expr, ExprKind, PatKind, QPath};
use rustc_lint::LintContext;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:**
/// Finds patterns that reimplement `Option::ok_or`.
///
/// **Why is this bad?**
/// Concise code helps focusing on behavior instead of boilerplate.
///
/// **Known problems:** None.
///
/// **Examples:**
/// ```rust
/// let foo: Option<i32> = None;
/// foo.map_or(Err("error"), |v| Ok(v));
///
/// let foo: Option<i32> = None;
/// foo.map_or(Err("error"), |v| Ok(v));
/// ```
///
/// Use instead:
/// ```rust
/// let foo: Option<i32> = None;
/// foo.ok_or("error");
/// ```
pub MANUAL_OK_OR,
pedantic,
"finds patterns that can be encoded more concisely with `Option::ok_or`"
}
declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]);
impl LateLintPass<'_> for ManualOkOr {
fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
if in_external_macro(cx.sess(), scrutinee.span) {
return;
}
if_chain! {
if let ExprKind::MethodCall(method_segment, _, args, _) = scrutinee.kind;
if method_segment.ident.name == sym!(map_or);
if args.len() == 3;
let method_receiver = &args[0];
let ty = cx.typeck_results().expr_ty(method_receiver);
if is_type_diagnostic_item(cx, ty, sym!(option_type));
let or_expr = &args[1];
if is_ok_wrapping(cx, &args[2]);
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
if match_qpath(err_path, &paths::RESULT_ERR);
if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span);
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
if let Some(indent) = indent_of(cx, scrutinee.span);
then {
let reindented_err_arg_snippet =
reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
span_lint_and_sugg(
cx,
MANUAL_OK_OR,
scrutinee.span,
"this pattern reimplements `Option::ok_or`",
"replace with",
format!(
"{}.ok_or({})",
method_receiver_snippet,
reindented_err_arg_snippet
),
Applicability::MachineApplicable,
);
}
}
}
}
fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
if let ExprKind::Path(ref qpath) = map_expr.kind {
if match_qpath(qpath, &paths::RESULT_OK) {
return true;
}
}
if_chain! {
if let ExprKind::Closure(_, _, body_id, ..) = map_expr.kind;
let body = cx.tcx.hir().body(body_id);
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
if match_qpath(ok_path, &paths::RESULT_OK);
if let ExprKind::Path(QPath::Resolved(_, ok_arg_path)) = ok_arg.kind;
if let def::Res::Local(ok_arg_path_id) = ok_arg_path.res;
then { param_id == ok_arg_path_id } else { false }
}
}

View file

@ -8,6 +8,7 @@ use rustc_lint::LintContext;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** /// **What it does:**
@ -97,9 +98,9 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if_chain! { if_chain! {
if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind;
let ty = cx.typeck_results().expr_ty(scrutinee); 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) 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) Some(Case::Result)
} else { } else {
None None

View file

@ -10,7 +10,7 @@ use rustc_middle::mir::Mutability;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::Span; use rustc_span::{sym, Span};
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests /// **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 args.len() == 2;
if method.ident.as_str() == "map"; if method.ident.as_str() == "map";
let ty = cx.typeck_results().expr_ty(&args[0]); 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; if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind;
let closure_body = cx.tcx.hir().body(body_id); let closure_body = cx.tcx.hir().body(body_id);
let closure_expr = remove_blocks(&closure_body.value); let closure_expr = remove_blocks(&closure_body.value);

View file

@ -7,6 +7,7 @@ use rustc_errors::Applicability;
use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind}; use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for instances of `map(f)` where `f` is the identity function. /// **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"; if args.len() == 2 && method.ident.as_str() == "map";
let caller_ty = cx.typeck_results().expr_ty(&args[0]); let caller_ty = cx.typeck_results().expr_ty(&args[0]);
if match_trait_method(cx, expr, &paths::ITERATOR) 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::result_type)
|| is_type_diagnostic_item(cx, caller_ty, sym!(option_type)); || is_type_diagnostic_item(cx, caller_ty, sym::option_type);
then { then {
Some(args) Some(args)
} else { } else {

View file

@ -6,6 +6,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for usage of `option.map(f)` where f is a function /// **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 var_arg = &map_args[0];
let (map_type, variant, lint) = 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) ("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) ("Result", "Ok", RESULT_MAP_UNIT_FN)
} else { } else {
return; return;

View file

@ -5,6 +5,7 @@ use rustc_hir::{Expr, ExprKind, LangItem, MatchSource};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`. /// **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 { fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let ty = cx.typeck_results().expr_ty(expr); let ty = cx.typeck_results().expr_ty(expr);
let ty = ty.peel_refs(); 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 { fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {

View file

@ -22,7 +22,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty, TyS}; use rustc_middle::ty::{self, Ty, TyS};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{Span, Spanned}; use rustc_span::source_map::{Span, Spanned};
use rustc_span::Symbol; use rustc_span::{sym, Symbol};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::collections::Bound; use std::collections::Bound;
@ -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<'_>]) { fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs(); 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 { for arm in arms {
if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind { 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)); 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_errors::Applicability;
use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath}; use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::sym;
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
@ -1552,7 +1553,7 @@ mod redundant_pattern_match {
if_chain! { if_chain! {
if keyword == "while"; if keyword == "while";
if let ExprKind::MethodCall(method_path, _, _, _) = op.kind; 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); if match_trait_method(cx, op, &paths::ITERATOR);
then { then {
return; return;

View file

@ -7,6 +7,7 @@ use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
use rustc_span::sym;
/// Checks for the `INEFFICIENT_TO_STRING` lint /// Checks for the `INEFFICIENT_TO_STRING` lint
pub fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) { 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; return true;
} }
if is_type_diagnostic_item(cx, ty, sym!(string_type)) { if is_type_diagnostic_item(cx, ty, sym::string_type) {
return true; return true;
} }

View file

@ -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, 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, 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, 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, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq,
SpanlessEq,
}; };
declare_clippy_lint! { declare_clippy_lint! {
@ -1291,8 +1290,8 @@ declare_clippy_lint! {
} }
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Warns when using `push_str` with a single-character string literal, /// **What it does:** Warns when using `push_str`/`insert_str` with a single-character string literal
/// and `push` with a `char` would work fine. /// where `push`/`insert` with a `char` would work fine.
/// ///
/// **Why is this bad?** It's less clear that we are pushing a single character. /// **Why is this bad?** It's less clear that we are pushing a single character.
/// ///
@ -1301,16 +1300,18 @@ declare_clippy_lint! {
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// let mut string = String::new(); /// let mut string = String::new();
/// string.insert_str(0, "R");
/// string.push_str("R"); /// string.push_str("R");
/// ``` /// ```
/// Could be written as /// Could be written as
/// ```rust /// ```rust
/// let mut string = String::new(); /// let mut string = String::new();
/// string.insert(0, 'R');
/// string.push('R'); /// string.push('R');
/// ``` /// ```
pub SINGLE_CHAR_PUSH_STR, pub SINGLE_CHAR_ADD_STR,
style, 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! { declare_clippy_lint! {
@ -1349,6 +1350,60 @@ declare_clippy_lint! {
"using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
} }
declare_clippy_lint! {
/// **What it does:** Checks for usage of `_.map(_).collect::<Result<(),_>()`.
///
/// **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic.
///
/// **Known problems:** None
///
/// **Example:**
///
/// ```rust
/// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
/// ```
/// Use instead:
/// ```rust
/// (0..3).try_for_each(|t| Err(t));
/// ```
pub MAP_COLLECT_RESULT_UNIT,
style,
"using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
}
declare_clippy_lint! {
/// **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator`
/// trait.
///
/// **Why is this bad?** It is recommended style to use collect. See
/// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// use std::iter::FromIterator;
///
/// let five_fives = std::iter::repeat(5).take(5);
///
/// let v = Vec::from_iter(five_fives);
///
/// assert_eq!(v, vec![5, 5, 5, 5, 5]);
/// ```
/// Use instead:
/// ```rust
/// let five_fives = std::iter::repeat(5).take(5);
///
/// let v: Vec<i32> = five_fives.collect();
///
/// assert_eq!(v, vec![5, 5, 5, 5, 5]);
/// ```
pub FROM_ITER_INSTEAD_OF_COLLECT,
style,
"use `.collect()` instead of `::from_iter()`"
}
declare_lint_pass!(Methods => [ declare_lint_pass!(Methods => [
UNWRAP_USED, UNWRAP_USED,
EXPECT_USED, EXPECT_USED,
@ -1370,7 +1425,7 @@ declare_lint_pass!(Methods => [
INEFFICIENT_TO_STRING, INEFFICIENT_TO_STRING,
NEW_RET_NO_SELF, NEW_RET_NO_SELF,
SINGLE_CHAR_PATTERN, SINGLE_CHAR_PATTERN,
SINGLE_CHAR_PUSH_STR, SINGLE_CHAR_ADD_STR,
SEARCH_IS_SOME, SEARCH_IS_SOME,
FILTER_NEXT, FILTER_NEXT,
SKIP_WHILE_NEXT, SKIP_WHILE_NEXT,
@ -1398,6 +1453,8 @@ declare_lint_pass!(Methods => [
FILETYPE_IS_FILE, FILETYPE_IS_FILE,
OPTION_AS_REF_DEREF, OPTION_AS_REF_DEREF,
UNNECESSARY_LAZY_EVALUATIONS, UNNECESSARY_LAZY_EVALUATIONS,
MAP_COLLECT_RESULT_UNIT,
FROM_ITER_INSTEAD_OF_COLLECT,
]); ]);
impl<'tcx> LateLintPass<'tcx> for Methods { 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"), ["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"), ["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"), ["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 { 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, _) => { 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_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); 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 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) { if match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
lint_single_char_push_string(cx, expr, args); 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<'_>) { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if_chain! { if_chain! {
if !in_external_macro(cx.tcx.sess, item.span); 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; if let TraitItemKind::Fn(_, _) = item.kind;
let ret_ty = return_ty(cx, item.hir_id); 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(); 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", "try this",
format!( format!(
"{}.unwrap_or_default()", "{}.unwrap_or_default()",
snippet_with_applicability(cx, self_expr.span, "_", &mut applicability) snippet_with_applicability(cx, self_expr.span, "..", &mut applicability)
), ),
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; return;
} }
} }
@ -1842,11 +1909,11 @@ fn lint_expect_fun_call(
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr,
hir::ExprKind::MethodCall(method_name, _, call_args, _) => { hir::ExprKind::MethodCall(method_name, _, call_args, _) => {
if call_args.len() == 1 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 arg_type = cx.typeck_results().expr_ty(&call_args[0]);
let base_type = arg_type.peel_refs(); 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] &call_args[0]
@ -1864,7 +1931,7 @@ fn lint_expect_fun_call(
// converted to string. // converted to string.
fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
let arg_ty = cx.typeck_results().expr_ty(arg); 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; return false;
} }
if let ty::Ref(_, ty, ..) = arg_ty.kind() { 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 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 { } else {
return; return;
@ -2119,7 +2186,7 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::
return; return;
}; };
let snippet = snippet_with_macro_callsite(cx, arg.span, "_"); let snippet = snippet_with_macro_callsite(cx, arg.span, "..");
span_lint_and_sugg( span_lint_and_sugg(
cx, 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 self_ty = cx.typeck_results().expr_ty(target).peel_refs();
let ref_str = if *self_ty.kind() == ty::Str { 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 { } else {
return; return;
@ -2155,9 +2222,9 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E
"try this", "try this",
format!( format!(
"{}.push_str({}{})", "{}.push_str({}{})",
snippet_with_applicability(cx, args[0].span, "_", &mut applicability), snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
ref_str, ref_str,
snippet_with_applicability(cx, target.span, "_", &mut applicability) snippet_with_applicability(cx, target.span, "..", &mut applicability)
), ),
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<'_>]) { fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); 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); lint_string_extend(cx, expr, args);
} }
} }
fn lint_iter_cloned_collect<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { fn lint_iter_cloned_collect<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) {
if_chain! { 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(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()); 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!( || matches!(
&cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(), &cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(),
ty::Array(_, _) ty::Array(_, _)
@ -2359,7 +2426,7 @@ fn lint_iter_nth<'tcx>(
let mut_str = if is_mut { "_mut" } else { "" }; 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() { let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])).is_some() {
"slice" "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" "Vec"
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vecdeque_type)) { } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vecdeque_type)) {
"VecDeque" "VecDeque"
@ -2404,7 +2471,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args:
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let expr_ty = cx.typeck_results().expr_ty(&get_args[0]); let expr_ty = cx.typeck_results().expr_ty(&get_args[0]);
let get_args_str = if get_args.len() > 1 { 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 { } else {
return; // not linting on a .get().unwrap() chain or variant 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() { let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() {
needs_ref = get_args_str.parse::<usize>().is_ok(); needs_ref = get_args_str.parse::<usize>().is_ok();
"slice" "slice"
} else if is_type_diagnostic_item(cx, expr_ty, sym!(vec_type)) { } else if is_type_diagnostic_item(cx, expr_ty, sym::vec_type) {
needs_ref = get_args_str.parse::<usize>().is_ok(); needs_ref = get_args_str.parse::<usize>().is_ok();
"Vec" "Vec"
} else if is_type_diagnostic_item(cx, expr_ty, sym!(vecdeque_type)) { } 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!( format!(
"{}{}[{}]", "{}{}[{}]",
borrow_str, 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 get_args_str
), ),
applicability, applicability,
@ -2480,7 +2547,7 @@ fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[
cx, cx,
ITER_SKIP_NEXT, ITER_SKIP_NEXT,
expr.span.trim_start(caller.span).unwrap(), expr.span.trim_start(caller.span).unwrap(),
"called `skip(x).next()` on an iterator", "called `skip(..).next()` on an iterator",
"use `nth` instead", "use `nth` instead",
hint, hint,
Applicability::MachineApplicable, Applicability::MachineApplicable,
@ -2498,7 +2565,7 @@ fn derefs_to_slice<'tcx>(
match ty.kind() { match ty.kind() {
ty::Slice(_) => true, ty::Slice(_) => true,
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), 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 ty::Array(_, size) => size
.try_eval_usize(cx.tcx, cx.param_env) .try_eval_usize(cx.tcx, cx.param_env)
.map_or(false, |size| size < 32), .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 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]) Some(&args[0])
} else { } else {
None None
@ -2533,9 +2600,9 @@ fn derefs_to_slice<'tcx>(
fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) { 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 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")) 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")) Some((UNWRAP_USED, "a Result", "Err"))
} else { } else {
None 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<'_>]) { fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Expr<'_>]) {
if_chain! { if_chain! {
// lint if the caller of `ok()` is a `Result` // 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]); let result_type = cx.typeck_results().expr_ty(&ok_args[0]);
if let Some(error_type) = get_error_type(cx, result_type); if let Some(error_type) = get_error_type(cx, result_type);
if has_debug_impl(error_type, cx); 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), _ => map_closure_ty.fn_sig(cx.tcx),
}; };
let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&map_closure_sig.output()); 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, _ => 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 // 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 func_snippet = snippet(cx, map_args[1].span, "..");
let hint = format!(".and_then({})", func_snippet); let hint = format!(".and_then({})", func_snippet);
span_lint_and_sugg( span_lint_and_sugg(
@ -2665,8 +2732,8 @@ fn lint_map_unwrap_or_else<'tcx>(
unwrap_args: &'tcx [hir::Expr<'_>], unwrap_args: &'tcx [hir::Expr<'_>],
) -> bool { ) -> bool {
// lint if the caller of `map()` is an `Option` // 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_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_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type);
if is_option || is_result { if is_option || is_result {
// Don't make a suggestion that may fail to compile due to mutably borrowing // 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 // lint message
let msg = if is_option { let msg = if is_option {
"called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling \ "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" `map_or_else(<g>, <f>)` instead"
} else { } else {
"called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling \ "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" `.map_or_else(<g>, <f>)` instead"
}; };
// get snippets for args to map() and unwrap_or_else() // get snippets for args to map() and unwrap_or_else()
let map_snippet = snippet(cx, map_args[1].span, ".."); 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 multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt(); let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt();
if same_span && !multiline { if same_span && !multiline {
span_lint_and_note( let var_snippet = snippet(cx, map_args[0].span, "..");
span_lint_and_sugg(
cx, cx,
MAP_UNWRAP_OR, MAP_UNWRAP_OR,
expr.span, expr.span,
msg, msg,
None, "try this",
&format!( format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet),
"replace `map({0}).unwrap_or_else({1})` with `map_or_else({1}, {0})`", Applicability::MachineApplicable,
map_snippet, unwrap_snippet,
),
); );
return true; return true;
} else if same_span && multiline { } 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 /// 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<'_>]) { 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_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_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: // There are two variants of this `map_or` lint:
// (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>` // (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>`
@ -2753,8 +2819,8 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
if is_option { if is_option {
let self_snippet = snippet(cx, map_or_args[0].span, ".."); let self_snippet = snippet(cx, map_or_args[0].span, "..");
let func_snippet = snippet(cx, map_or_args[2].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 \ let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
`and_then(f)` instead"; `and_then(..)` instead";
( (
OPTION_MAP_OR_NONE, OPTION_MAP_OR_NONE,
msg, 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<'_>]) { 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 // lint if caller of `.filter().next()` is an Iterator
if match_trait_method(cx, expr, &paths::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 \ let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
`.find(p)` instead."; `.find(..)` instead.";
let filter_snippet = snippet(cx, filter_args[1].span, ".."); let filter_snippet = snippet(cx, filter_args[1].span, "..");
if filter_snippet.lines().count() <= 1 { if filter_snippet.lines().count() <= 1 {
let iter_snippet = snippet(cx, filter_args[0].span, "..");
// add note if not multi-line // add note if not multi-line
span_lint_and_note( span_lint_and_sugg(
cx, cx,
FILTER_NEXT, FILTER_NEXT,
expr.span, expr.span,
msg, msg,
None, "try this",
&format!("replace `filter({0}).next()` with `find({0})`", filter_snippet), format!("{}.find({})", iter_snippet, filter_snippet),
Applicability::MachineApplicable,
); );
} else { } else {
span_lint(cx, FILTER_NEXT, expr.span, msg); span_lint(cx, FILTER_NEXT, expr.span, msg);
@ -2823,9 +2891,9 @@ fn lint_skip_while_next<'tcx>(
cx, cx,
SKIP_WHILE_NEXT, SKIP_WHILE_NEXT,
expr.span, expr.span,
"called `skip_while(p).next()` on an `Iterator`", "called `skip_while(<p>).next()` on an `Iterator`",
None, None,
"this is more succinctly expressed by calling `.find(!p)` instead", "this is more succinctly expressed by calling `.find(!<p>)` instead",
); );
} }
} }
@ -2839,7 +2907,7 @@ fn lint_filter_map<'tcx>(
) { ) {
// lint if caller of `.filter().map()` is an Iterator // lint if caller of `.filter().map()` is an Iterator
if match_trait_method(cx, expr, &paths::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"; let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead";
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); 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` /// 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<'_>]) { 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) { 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 \ let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
`.find_map(p)` instead."; `.find_map(..)` instead.";
let filter_snippet = snippet(cx, filter_args[1].span, ".."); let filter_snippet = snippet(cx, filter_args[1].span, "..");
if filter_snippet.lines().count() <= 1 { if filter_snippet.lines().count() <= 1 {
span_lint_and_note( let iter_snippet = snippet(cx, filter_args[0].span, "..");
span_lint_and_sugg(
cx, cx,
FILTER_MAP_NEXT, FILTER_MAP_NEXT,
expr.span, expr.span,
msg, msg,
None, "try this",
&format!("replace `filter_map({0}).next()` with `find_map({0})`", filter_snippet), format!("{}.find_map({})", iter_snippet, filter_snippet),
Applicability::MachineApplicable,
); );
} else { } else {
span_lint(cx, FILTER_MAP_NEXT, expr.span, msg); 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 // lint if caller of `.filter().map()` is an Iterator
if match_trait_method(cx, &map_args[0], &paths::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"; let hint = "this is more succinctly expressed by calling `.find_map(..)` instead";
span_lint_and_help(cx, FIND_MAP, expr.span, msg, None, hint); 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 // lint if caller of `.filter().map()` is an Iterator
if match_trait_method(cx, expr, &paths::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"; 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); 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 // lint if caller of `.filter().flat_map()` is an Iterator
if match_trait_method(cx, expr, &paths::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(..)` \ let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
and filtering by returning `iter::empty()`"; and filtering by returning `iter::empty()`";
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); 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 // lint if caller of `.filter_map().flat_map()` is an Iterator
if match_trait_method(cx, expr, &paths::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(..)` \ let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
and filtering by returning `iter::empty()`"; and filtering by returning `iter::empty()`";
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); 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 arg_char.len() == 1;
if let hir::ExprKind::Path(ref qpath) = fun.kind; if let hir::ExprKind::Path(ref qpath) = fun.kind;
if let Some(segment) = single_segment_path(qpath); if let Some(segment) = single_segment_path(qpath);
if segment.ident.name == sym!(Some); if segment.ident.name == sym::Some;
then { then {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs(); let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
@ -3092,9 +3162,9 @@ fn lint_chars_cmp(
"like this", "like this",
format!("{}{}.{}({})", format!("{}{}.{}({})",
if info.eq { "" } else { "!" }, 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, suggest,
snippet_with_applicability(cx, arg_char[0].span, "_", &mut applicability)), snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
applicability, applicability,
); );
@ -3141,7 +3211,7 @@ fn lint_chars_cmp_with_unwrap<'tcx>(
"like this", "like this",
format!("{}{}.{}('{}')", format!("{}{}.{}('{}')",
if info.eq { "" } else { "!" }, 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, suggest,
c), c),
applicability, applicability,
@ -3177,7 +3247,7 @@ fn get_hint_if_single_char_arg(
if let hir::ExprKind::Lit(lit) = &arg.kind; if let hir::ExprKind::Lit(lit) = &arg.kind;
if let ast::LitKind::Str(r, style) = lit.node; if let ast::LitKind::Str(r, style) = lit.node;
let string = r.as_str(); let string = r.as_str();
if string.len() == 1; if string.chars().count() == 1;
then { then {
let snip = snippet_with_applicability(cx, arg.span, &string, applicability); let snip = snippet_with_applicability(cx, arg.span, &string, applicability);
let ch = if let ast::StrStyle::Raw(nhash) = style { 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<'_>]) { fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { 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); let sugg = format!("{}.push({})", base_string_snippet, extension_string);
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
SINGLE_CHAR_PUSH_STR, SINGLE_CHAR_ADD_STR,
expr.span, expr.span,
"calling `push_str()` using a single-character string literal", "calling `push_str()` using a single-character string literal",
"consider using `push` with a character 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. /// Checks for the `USELESS_ASREF` lint.
fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) { 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" // 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, expr.span,
&format!("this call to `{}` does nothing", call_name), &format!("this call to `{}` does nothing", call_name),
"try this", "try this",
snippet_with_applicability(cx, recvr.span, "_", &mut applicability).to_string(), snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(),
applicability, 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 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]); 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; 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<T, E>` type, return its error type (`E`). /// Given a `Result<T, E>` type, return its error type (`E`).
fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> { fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
match ty.kind() { 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, _ => 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); 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 { fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
expected.constness == actual.constness expected.constness == actual.constness
&& expected.unsafety == actual.unsafety && expected.unsafety == actual.unsafety

View file

@ -7,7 +7,7 @@ use rustc_hir::{self, HirId, Path};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol; use rustc_span::{sym, Symbol};
use super::MAP_UNWRAP_OR; use super::MAP_UNWRAP_OR;
@ -20,7 +20,7 @@ pub(super) fn lint<'tcx>(
map_span: Span, map_span: Span,
) { ) {
// lint if the caller of `map()` is an `Option` // 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])) { if !is_copy(cx, cx.typeck_results().expr_ty(&unwrap_args[1])) {
// Do not lint if the `map` argument uses identifiers in the `map` // Do not lint if the `map` argument uses identifiers in the `map`
// argument that are also used in the `unwrap_or` argument // argument that are also used in the `unwrap_or` argument
@ -53,15 +53,15 @@ pub(super) fn lint<'tcx>(
// lint message // lint message
// comparing the snippet from source to raw text ("None") below is safe // comparing the snippet from source to raw text ("None") below is safe
// because we already have checked the type. // because we already have checked the type.
let arg = if unwrap_snippet == "None" { "None" } else { "a" }; let arg = if unwrap_snippet == "None" { "None" } else { "<a>" };
let unwrap_snippet_none = unwrap_snippet == "None"; let unwrap_snippet_none = unwrap_snippet == "None";
let suggest = if unwrap_snippet_none { let suggest = if unwrap_snippet_none {
"and_then(f)" "and_then(<f>)"
} else { } else {
"map_or(a, f)" "map_or(<a>, <f>)"
}; };
let msg = &format!( let msg = &format!(
"called `map(f).unwrap_or({})` on an `Option` value. \ "called `map(<f>).unwrap_or({})` on an `Option` value. \
This can be done more directly by calling `{}` instead", This can be done more directly by calling `{}` instead",
arg, suggest arg, suggest
); );

View file

@ -3,6 +3,7 @@ use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::sym;
use super::UNNECESSARY_LAZY_EVALUATIONS; use super::UNNECESSARY_LAZY_EVALUATIONS;
@ -14,8 +15,8 @@ pub(super) fn lint<'tcx>(
args: &'tcx [hir::Expr<'_>], args: &'tcx [hir::Expr<'_>],
simplify_using: &str, simplify_using: &str,
) { ) {
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_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)); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::result_type);
if is_option || is_result { if is_option || is_result {
if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {

View file

@ -7,6 +7,7 @@ use rustc_hir::{
StmtKind, TyKind, UnOp, StmtKind, TyKind, UnOp,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::hygiene::DesugaringKind; use rustc_span::hygiene::DesugaringKind;
@ -271,13 +272,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
k: FnKind<'tcx>, k: FnKind<'tcx>,
decl: &'tcx FnDecl<'_>, decl: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>, body: &'tcx Body<'_>,
_: Span, span: Span,
_: HirId, _: HirId,
) { ) {
if let FnKind::Closure(_) = k { if let FnKind::Closure(_) = k {
// Does not apply to closures // Does not apply to closures
return; return;
} }
if in_external_macro(cx.tcx.sess, span) {
return;
}
for arg in iter_input_pats(decl, body) { for arg in iter_input_pats(decl, body) {
if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind { if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind {
span_lint( span_lint(
@ -293,13 +297,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if_chain! { if_chain! {
if !in_external_macro(cx.tcx.sess, stmt.span);
if let StmtKind::Local(ref local) = stmt.kind; if let StmtKind::Local(ref local) = stmt.kind;
if let PatKind::Binding(an, .., name, None) = local.pat.kind; if let PatKind::Binding(an, .., name, None) = local.pat.kind;
if let Some(ref init) = local.init; if let Some(ref init) = local.init;
if !higher::is_from_for_desugar(local); if !higher::is_from_for_desugar(local);
then { then {
if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut { 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, "..") Sugg::hir_with_macro_callsite(cx, init, "..")
} else { } else {
Sugg::hir(cx, init, "..") Sugg::hir(cx, init, "..")
@ -310,7 +317,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
("", sugg_init.addr()) ("", sugg_init.addr())
}; };
let tyopt = if let Some(ref ty) = local.ty { 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 { } else {
String::new() String::new()
}; };
@ -326,7 +333,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
"try", "try",
format!( format!(
"let {name}{tyopt} = {initref};", "let {name}{tyopt} = {initref};",
name=snippet(cx, name.span, "_"), name=snippet(cx, name.span, ".."),
tyopt=tyopt, tyopt=tyopt,
initref=initref, initref=initref,
), ),

View file

@ -14,6 +14,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Warns if there is missing doc for any documentable item /// **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]) { fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, attrs: &'tcx [ast::Attribute]) {
let doc_hidden = self.doc_hidden() let doc_hidden = self.doc_hidden()
|| attrs.iter().any(|attr| { || attrs.iter().any(|attr| {
attr.has_name(sym!(doc)) attr.has_name(sym::doc)
&& match attr.meta_item_list() { && match attr.meta_item_list() {
None => false, 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); self.doc_hidden_stack.push(doc_hidden);
@ -128,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
hir::ItemKind::Enum(..) => "an enum", hir::ItemKind::Enum(..) => "an enum",
hir::ItemKind::Fn(..) => { hir::ItemKind::Fn(..) => {
// ignore main() // ignore main()
if it.ident.name == sym!(main) { if it.ident.name == sym::main {
let def_id = it.hir_id.owner; let def_id = it.hir_id.owner;
let def_key = cx.tcx.hir().def_key(def_id); let def_key = cx.tcx.hir().def_key(def_id);
if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) { if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) {

View file

@ -4,6 +4,7 @@ use rustc_hir as hir;
use rustc_lint::{self, LateContext, LateLintPass, LintContext}; use rustc_lint::{self, LateContext, LateLintPass, LintContext};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** it lints if an exported function, method, trait method with default impl, /// **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) { 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 { if !has_inline {
span_lint( span_lint(
cx, cx,

View file

@ -10,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for address of operations (`&`) that are going to /// **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<'_>) { 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()); debug_assert!(self.derived_item.is_none());
self.derived_item = Some(item.hir_id); self.derived_item = Some(item.hir_id);
} }

View file

@ -13,7 +13,7 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TypeFoldable}; use rustc_middle::ty::{self, TypeFoldable};
use rustc_session::{declare_lint_pass, declare_tool_lint}; 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_target::spec::abi::Abi;
use rustc_trait_selection::traits; use rustc_trait_selection::traits;
use rustc_trait_selection::traits::misc::can_type_implement_copy; 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); let deref_span = spans_need_deref.get(&canonical_id);
if_chain! { 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) = if let Some(clone_spans) =
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]); get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]);
if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind; if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind;
if let Some(elem_ty) = path.segments.iter() 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()) .and_then(|ps| ps.args.as_ref())
.map(|params| params.args.iter().find_map(|arg| match arg { .map(|params| params.args.iter().find_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty), 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) = if let Some(clone_spans) =
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) { get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
diag.span_suggestion( diag.span_suggestion(
@ -302,7 +302,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
/// Functions marked with these attributes must have the exact signature. /// Functions marked with these attributes must have the exact signature.
fn requires_exact_signature(attrs: &[Attribute]) -> bool { fn requires_exact_signature(attrs: &[Attribute]) -> bool {
attrs.iter().any(|attr| { 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() .iter()
.any(|&allow| attr.has_name(allow)) .any(|&allow| attr.has_name(allow))
}) })

View file

@ -9,6 +9,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{Ty, TyS}; use rustc_middle::ty::{Ty, TyS};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for types with a `fn new() -> Self` method and no /// **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` // impl of `Default`
return; 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_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); let self_ty = cx.tcx.type_of(self_def_id);
if_chain! { if_chain! {

View file

@ -7,6 +7,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::sym;
use rustc_span::symbol::{Ident, Symbol}; use rustc_span::symbol::{Ident, Symbol};
use std::cmp::Ordering; 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) { 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 { let mut visitor = SimilarNamesLocalVisitor {
names: Vec::new(), names: Vec::new(),
cx, cx,

View file

@ -8,6 +8,7 @@ use rustc_errors::Applicability;
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** /// **What it does:**
@ -66,7 +67,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind {
path.ident.name.to_ident_string() == "ok" 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 { } else {
false false
} }

View file

@ -5,7 +5,7 @@ use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span; use rustc_span::{sym, Span};
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result. /// **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, hir_id: hir::HirId,
) { ) {
if !matches!(fn_kind, FnKind::Closure(_)) 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); lint_impl_body(cx, span, body);
} }

View file

@ -3,6 +3,7 @@ use if_chain::if_chain;
use rustc_hir::{Item, ItemKind}; use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for manual re-implementations of `PartialEq::ne`. /// **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; if trait_ref.path.res.def_id() == eq_trait;
then { then {
for impl_item in impl_items { for impl_item in impl_items {
if impl_item.ident.name == sym!(ne) { if impl_item.ident.name == sym::ne {
span_lint_hir( span_lint_hir(
cx, cx,
PARTIALEQ_NE_IMPL, PARTIALEQ_NE_IMPL,

View file

@ -10,7 +10,7 @@ use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutabil
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass}; 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::abi::LayoutOf;
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
use rustc_target::spec::Target; use rustc_target::spec::Target;
@ -230,8 +230,8 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue {
} }
for a in attrs { for a in attrs {
if let Some(meta_items) = a.meta_item_list() { if let Some(meta_items) = a.meta_item_list() {
if a.has_name(sym!(proc_macro_derive)) if a.has_name(sym::proc_macro_derive)
|| (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) || (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always))
{ {
return; return;
} }

View file

@ -15,7 +15,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::MultiSpan; use rustc_span::{sym, MultiSpan};
use std::borrow::Cow; use std::borrow::Cow;
declare_clippy_lint! { 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 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; let mut ty_snippet = None;
if_chain! { if_chain! {
if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind; 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", "")]) { if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
span_lint_and_then( span_lint_and_then(
cx, cx,

View file

@ -3,6 +3,7 @@ use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
use std::fmt; use std::fmt;
declare_clippy_lint! { declare_clippy_lint! {
@ -92,7 +93,7 @@ fn expr_as_ptr_offset_call<'tcx>(
) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> {
if let ExprKind::MethodCall(ref path_segment, _, ref args, _) = expr.kind { if let ExprKind::MethodCall(ref path_segment, _, ref args, _) = expr.kind {
if is_expr_ty_raw_ptr(cx, &args[0]) { 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)); return Some((&args[0], &args[1], Method::Offset));
} }
if path_segment.ident.name == sym!(wrapping_offset) { if path_segment.ident.name == sym!(wrapping_offset) {

View file

@ -4,6 +4,7 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
use crate::utils::sugg::Sugg; use crate::utils::sugg::Sugg;
use crate::utils::{ use crate::utils::{
@ -143,7 +144,7 @@ impl QuestionMark {
fn is_option(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { fn is_option(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expression); 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 { fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {

View file

@ -7,6 +7,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{Span, Spanned}; use rustc_span::source_map::{Span, Spanned};
use rustc_span::sym;
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use std::cmp::Ordering; use std::cmp::Ordering;
@ -304,7 +305,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args:
if_chain! { if_chain! {
// `.iter()` call // `.iter()` call
if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = *iter; 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()` // range expression in `.zip()` call: `0..x.len()`
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
if is_integer_const(cx, start, 0); if is_integer_const(cx, start, 0);

View file

@ -17,6 +17,7 @@ use rustc_middle::ty::{self, fold::TypeVisitor, Ty};
use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor}; use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{BytePos, Span}; use rustc_span::source_map::{BytePos, Span};
use rustc_span::sym;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::ops::ControlFlow; 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) 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_OWNED_METHOD)
|| (match_def_path(cx, fn_def_id, &paths::TO_STRING_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 let from_deref = !from_borrow
&& (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF) && (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); self.possible_borrower.add(borrowed.local, lhs);
}, },
other => { 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; return;
} }
rvalue_locals(other, |rhs| { rvalue_locals(other, |rhs| {

View file

@ -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,
);
}
}
}
}

View file

@ -5,6 +5,7 @@ use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types. /// **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#""...""#)), format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)),
Applicability::MachineApplicable, 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( span_lint_and_sugg(
cx, cx,
REPEAT_ONCE, REPEAT_ONCE,

View file

@ -9,6 +9,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; 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}; 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 { 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>) { fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {

View file

@ -4,6 +4,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_span::sym;
use if_chain::if_chain; use if_chain::if_chain;
@ -154,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd {
} }
fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { 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 { fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {

View file

@ -8,6 +8,7 @@ use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for manual swapping. /// **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(_)) if matches!(ty.kind(), ty::Slice(_))
|| matches!(ty.kind(), ty::Array(_, _)) || 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)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type))
{ {
return Slice::Swappable(lhs1, idx1, idx2); return Slice::Swappable(lhs1, idx1, idx2);

View file

@ -9,6 +9,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for usages of `Err(x)?`. /// **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<Ty<'tcx>> { fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
if_chain! { if_chain! {
if let ty::Adt(_, subst) = ty.kind(); 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); let err_ty = subst.type_at(1);
then { then {
Some(err_ty) 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); let ready_ty = subst.type_at(0);
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); 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); let err_ty = ready_subst.type_at(1);
then { 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); let ready_ty = subst.type_at(0);
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); 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); let some_ty = ready_subst.type_at(0);
if let ty::Adt(some_def, some_subst) = some_ty.kind(); 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); let err_ty = some_subst.type_at(1);
then { then {

View file

@ -10,9 +10,9 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::{ use rustc_hir::{
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId,
ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt,
TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, StmtKind, SyntheticTyParamKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
}; };
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
@ -522,7 +522,7 @@ impl Types {
); );
return; // don't recurse into the type 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! { if_chain! {
// Get the _ part of Vec<_> // Get the _ part of Vec<_>
if let Some(ref last) = last_path_segment(qpath).args; if let Some(ref last) = last_path_segment(qpath).args;
@ -559,7 +559,7 @@ impl Types {
return; // don't recurse into the type 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() { if match_type_parameter(cx, qpath, &paths::OPTION).is_some() {
span_lint( span_lint(
cx, cx,
@ -678,17 +678,30 @@ impl Types {
// details. // details.
return; 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( span_lint_and_sugg(
cx, cx,
BORROWED_BOX, BORROWED_BOX,
hir_ty.span, hir_ty.span,
"you seem to be trying to use `&Box<T>`. Consider using just `&T`", "you seem to be trying to use `&Box<T>`. Consider using just `&T`",
"try", "try",
format!( suggestion,
"&{}{}",
ltopt,
&snippet(cx, inner.span, "..")
),
// To make this `MachineApplicable`, at least one needs to check if it isn't a trait item // 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; // because the trait impls of it will break otherwise;
// and there may be other cases that result in invalid code. // and there may be other cases that result in invalid code.
@ -721,6 +734,21 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool {
false false
} }
fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> {
if_chain! {
if let Some(did) = qpath_res(cx, qpath, id).opt_def_id();
if let Some(node) = cx.tcx.hir().get_if_local(did);
if let Node::GenericParam(generic_param) = node;
if let GenericParamKind::Type { synthetic, .. } = generic_param.kind;
if synthetic == Some(SyntheticTyParamKind::ImplTrait);
then {
Some(generic_param.bounds)
} else {
None
}
}
}
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for binding a unit value. /// **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() { if names.is_empty() {
return false; 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; 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 match_path(ty_path, &paths::HASHMAP) {
if method.ident.name == sym!(new) { if method.ident.name == sym::new {
self.suggestions self.suggestions
.insert(e.span, "HashMap::default()".to_string()); .insert(e.span, "HashMap::default()".to_string());
} else if method.ident.name == sym!(with_capacity) { } 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) { } else if match_path(ty_path, &paths::HASHSET) {
if method.ident.name == sym!(new) { if method.ident.name == sym::new {
self.suggestions self.suggestions
.insert(e.span, "HashSet::default()".to_string()); .insert(e.span, "HashSet::default()".to_string());
} else if method.ident.name == sym!(with_capacity) { } else if method.ident.name == sym!(with_capacity) {

View file

@ -6,6 +6,7 @@ use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegme
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, subst::GenericArgKind}; use rustc_middle::ty::{self, subst::GenericArgKind};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
declare_clippy_lint! { declare_clippy_lint! {
@ -175,7 +176,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
if let name = name_ident.ident.name.to_ident_string(); if let name = name_ident.ident.name.to_ident_string();
if name == "sort_by" || name == "sort_unstable_by"; if name == "sort_by" || name == "sort_unstable_by";
if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; 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 closure_body = cx.tcx.hir().body(*closure_body_id);
if let &[ if let &[
Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},

View file

@ -123,6 +123,17 @@ fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
fn_source fn_source
.rfind("->") .rfind("->")
.map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
let mut rpos = rpos;
let chars: Vec<char> = 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)] #[allow(clippy::cast_possible_truncation)]
ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),

View file

@ -11,6 +11,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::Ty; use rustc_middle::ty::Ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail. /// **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.
@ -92,11 +93,11 @@ fn collect_unwrap_info<'tcx>(
invert: bool, invert: bool,
) -> Vec<UnwrapInfo<'tcx>> { ) -> Vec<UnwrapInfo<'tcx>> {
fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { 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 { 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 { 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_chain! {
if let ExprKind::MethodCall(ref method_name, _, ref args, _) = expr.kind; if let ExprKind::MethodCall(ref method_name, _, ref args, _) = expr.kind;
if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].kind; if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].kind;
if [sym!(unwrap), sym!(unwrap_err)].contains(&method_name.ident.name); if [sym::unwrap, sym!(unwrap_err)].contains(&method_name.ident.name);
let call_to_unwrap = method_name.ident.name == sym!(unwrap); let call_to_unwrap = method_name.ident.name == sym::unwrap;
if let Some(unwrappable) = self.unwrappables.iter() if let Some(unwrappable) = self.unwrappables.iter()
.find(|u| u.ident.res == path.res); .find(|u| u.ident.res == path.res);
// Span contexts should not differ with the conditional branch // Span contexts should not differ with the conditional branch

View file

@ -5,7 +5,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span; use rustc_span::{sym, Span};
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()` /// **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 // first check if it's a method or function
if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind;
// checking if its return type is `result` or `option` // 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)) 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)); || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::option_type);
then { then {
lint_impl_body(cx, impl_item.span, impl_item); 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` // check for `expect`
if let Some(arglists) = method_chain_args(expr, &["expect"]) { if let Some(arglists) = method_chain_args(expr, &["expect"]) {
let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); 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)) if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type)
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) || is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type)
{ {
self.result.push(expr.span); self.result.push(expr.span);
} }
@ -92,8 +92,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
// check for `unwrap` // check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); 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)) if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type)
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) || is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type)
{ {
self.result.push(expr.span); self.result.push(expr.span);
} }

View file

@ -9,6 +9,7 @@ use rustc_hir::{Expr, ExprKind, HirId, MatchSource};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TyS}; use rustc_middle::ty::{self, TyS};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`,`IntoIter` calls /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`,`IntoIter` calls
@ -106,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
if_chain! { if_chain! {
let a = cx.typeck_results().expr_ty(e); let a = cx.typeck_results().expr_ty(e);
let b = cx.typeck_results().expr_ty(&args[0]); 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 ty::Adt(_, substs) = a.kind();
if let Some(a_type) = substs.types().next(); if let Some(a_type) = substs.types().next();
if TyS::same_type(a_type, b); if TyS::same_type(a_type, b);
@ -136,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
then { then {
if_chain! { if_chain! {
if match_def_path(cx, def_id, &paths::TRY_FROM); 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 ty::Adt(_, substs) = a.kind();
if let Some(a_type) = substs.types().next(); if let Some(a_type) = substs.types().next();
if TyS::same_type(a_type, b); if TyS::same_type(a_type, b);

View file

@ -225,7 +225,7 @@ declare_clippy_lint! {
/// ///
/// Good: /// Good:
/// ```rust,ignore /// ```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, pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
internal, internal,
@ -724,7 +724,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
expr.span, expr.span,
"usage of `utils::match_type()` on a type diagnostic item", "usage of `utils::match_type()` on a type diagnostic item",
"try", "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, Applicability::MaybeIncorrect,
); );
} }

View file

@ -52,6 +52,7 @@ use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::original_sp; use rustc_span::source_map::original_sp;
use rustc_span::sym as rustc_sym;
use rustc_span::symbol::{self, kw, Symbol}; use rustc_span::symbol::{self, kw, Symbol};
use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; use rustc_span::{BytePos, Pos, Span, DUMMY_SP};
use rustc_target::abi::Integer; 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 /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
/// implementations have. /// implementations have.
pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { 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. /// Remove blocks around an expression.

View file

@ -44,6 +44,7 @@ pub const FN: [&str; 3] = ["core", "ops", "Fn"];
pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"];
pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; 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 FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; 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 HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; 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: [&str; 3] = ["core", "convert", "Into"];
pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"]; pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_READ: [&str; 3] = ["std", "io", "Read"];

View file

@ -10,8 +10,7 @@ use rustc_lexer::unescape::{self, EscapeError};
use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_parse::parser; use rustc_parse::parser;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::Symbol; use rustc_span::{sym, BytePos, Span, Symbol};
use rustc_span::{BytePos, Span};
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** This lint warns when you use `println!("")` to /// **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") .expect("path has at least one segment")
.ident .ident
.name; .name;
if trait_name == sym!(Debug) { if trait_name == sym::Debug {
self.in_debug_impl = true; self.in_debug_impl = true;
} }
} }

View file

@ -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 According to [the rustc-dev-guide], the text should be matter of fact and avoid
capitalization and periods, unless multiple sentences are needed. capitalization and periods, unless multiple sentences are needed.
When code or an identifier must appear in a message or label, it should be 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 [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 [diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs

View file

@ -298,6 +298,13 @@ vec![
deprecation: None, deprecation: None,
module: "comparison_chain", 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 { Lint {
name: "copy_iterator", name: "copy_iterator",
group: "pedantic", group: "pedantic",
@ -352,7 +359,7 @@ vec![
group: "pedantic", group: "pedantic",
desc: "checks for literal calls to `Default::default()`", desc: "checks for literal calls to `Default::default()`",
deprecation: None, deprecation: None,
module: "default_trait_access", module: "default",
}, },
Lint { Lint {
name: "deprecated_cfg_attr", name: "deprecated_cfg_attr",
@ -620,6 +627,13 @@ vec![
deprecation: None, deprecation: None,
module: "fallible_impl_from", 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 { Lint {
name: "filetype_is_file", name: "filetype_is_file",
group: "restriction", group: "restriction",
@ -746,6 +760,13 @@ vec![
deprecation: None, deprecation: None,
module: "drop_forget_ref", 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 { Lint {
name: "future_not_send", name: "future_not_send",
group: "nursery", group: "nursery",
@ -1173,6 +1194,13 @@ vec![
deprecation: None, deprecation: None,
module: "manual_non_exhaustive", 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 { Lint {
name: "manual_range_contains", name: "manual_range_contains",
group: "style", group: "style",
@ -1222,6 +1250,13 @@ vec![
deprecation: None, deprecation: None,
module: "map_clone", module: "map_clone",
}, },
Lint {
name: "map_collect_result_unit",
group: "style",
desc: "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`",
deprecation: None,
module: "methods",
},
Lint { Lint {
name: "map_entry", name: "map_entry",
group: "perf", group: "perf",
@ -2013,6 +2048,13 @@ vec![
deprecation: None, deprecation: None,
module: "reference", module: "reference",
}, },
Lint {
name: "ref_option_ref",
group: "pedantic",
desc: "use `Option<&T>` instead of `&Option<&T>`",
deprecation: None,
module: "ref_option_ref",
},
Lint { Lint {
name: "repeat_once", name: "repeat_once",
group: "complexity", group: "complexity",
@ -2133,16 +2175,16 @@ vec![
module: "non_expressive_names", module: "non_expressive_names",
}, },
Lint { Lint {
name: "single_char_pattern", name: "single_char_add_str",
group: "perf", group: "style",
desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`", desc: "`push_str()` or `insert_str()` used with a single-character string literal as parameter",
deprecation: None, deprecation: None,
module: "methods", module: "methods",
}, },
Lint { Lint {
name: "single_char_push_str", name: "single_char_pattern",
group: "style", group: "perf",
desc: "`push_str()` used with a single-character string literal as parameter", desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`",
deprecation: None, deprecation: None,
module: "methods", module: "methods",
}, },

View file

@ -56,3 +56,17 @@ macro_rules! option_env_unwrap_external {
option_env!($env).expect($message) 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) {}
};
}

View file

@ -3,6 +3,8 @@
#![allow(unused_variables)] #![allow(unused_variables)]
#![allow(dead_code)] #![allow(dead_code)]
use std::fmt::Display;
pub fn test1(foo: &mut Box<bool>) { pub fn test1(foo: &mut Box<bool>) {
// Although this function could be changed to "&mut bool", // Although this function could be changed to "&mut bool",
// avoiding the Box, mutable references to boxes are not // 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(); *boxed_slice = data.into_boxed_slice();
} }
// The suggestion should include proper parentheses to avoid a syntax error.
pub fn test14(_display: &Box<dyn Display>) {}
pub fn test15(_display: &Box<dyn Display + Send>) {}
pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
pub fn test17(_display: &Box<impl Display>) {}
pub fn test18(_display: &Box<impl Display + Send>) {}
pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
// This exists only to check what happens when parentheses are already present.
// Even though the current implementation doesn't put extra parentheses,
// it's fine that unnecessary parentheses appear in the future for some reason.
pub fn test20(_display: &Box<(dyn Display + Send)>) {}
fn main() { fn main() {
test1(&mut Box::new(false)); test1(&mut Box::new(false));
test2(); test2();

View file

@ -1,5 +1,5 @@
error: you seem to be trying to use `&Box<T>`. Consider using just `&T` error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:19:14 --> $DIR/borrow_box.rs:21:14
| |
LL | let foo: &Box<bool>; LL | let foo: &Box<bool>;
| ^^^^^^^^^^ help: try: `&bool` | ^^^^^^^^^^ help: try: `&bool`
@ -11,16 +11,58 @@ LL | #![deny(clippy::borrowed_box)]
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
error: you seem to be trying to use `&Box<T>`. Consider using just `&T` error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:23:10 --> $DIR/borrow_box.rs:25:10
| |
LL | foo: &'a Box<bool>, LL | foo: &'a Box<bool>,
| ^^^^^^^^^^^^^ help: try: `&'a bool` | ^^^^^^^^^^^^^ help: try: `&'a bool`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T` error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:27:17 --> $DIR/borrow_box.rs:29:17
| |
LL | fn test4(a: &Box<bool>); LL | fn test4(a: &Box<bool>);
| ^^^^^^^^^^ help: try: `&bool` | ^^^^^^^^^^ help: try: `&bool`
error: aborting due to 3 previous errors error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:95:25
|
LL | pub fn test14(_display: &Box<dyn Display>) {}
| ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:96:25
|
LL | pub fn test15(_display: &Box<dyn Display + Send>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:97:29
|
LL | pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:99:25
|
LL | pub fn test17(_display: &Box<impl Display>) {}
| ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:100:25
|
LL | pub fn test18(_display: &Box<impl Display + Send>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:101:29
|
LL | pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)`
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:106:25
|
LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`
error: aborting due to 10 previous errors

View file

@ -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];
}

View file

@ -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];
}

View file

@ -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

View file

@ -0,0 +1,16 @@
// originally from glacier/fixed/77218.rs
// ice while adjusting...
pub struct Cache {
data: Vec<i32>,
}
pub fn list_data(cache: &Cache, key: usize) {
for reference in vec![1, 2, 3] {
if
/* let */
Some(reference) = cache.data.get(key) {
unimplemented!()
}
}
}

View file

@ -0,0 +1,27 @@
error[E0601]: `main` function not found in crate `ice_6250`
--> $DIR/ice-6250.rs:4:1
|
LL | / pub struct Cache {
LL | | data: Vec<i32>,
LL | | }
LL | |
... |
LL | | }
LL | | }
| |_^ consider adding a `main` function to `$DIR/ice-6250.rs`
error[E0308]: mismatched types
--> $DIR/ice-6250.rs:12:9
|
LL | Some(reference) = cache.data.get(key) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
|
help: you might have meant to use pattern matching
|
LL | let Some(reference) = cache.data.get(key) {
| ^^^
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0308, E0601.
For more information about an error, try `rustc --explain E0308`.

View file

@ -0,0 +1,6 @@
// originally from glacier/fixed/77329.rs
// assertion failed: `(left == right) ; different DefIds
fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
std::iter::empty()
}

View file

@ -0,0 +1,43 @@
error[E0601]: `main` function not found in crate `ice_6251`
--> $DIR/ice-6251.rs:4:1
|
LL | / fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
LL | | std::iter::empty()
LL | | }
| |_^ consider adding a `main` function to `$DIR/ice-6251.rs`
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/ice-6251.rs:4:45
|
LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
| ^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `[u8]`
= help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size
|
LL | fn bug<T>() -> impl Iterator<Item = [(); { |&x: [u8]| x }]> {
| ^
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/ice-6251.rs:4:54
|
LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
| ^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `[u8]`
= note: the return type of a function must have a statically known size
error[E0308]: mismatched types
--> $DIR/ice-6251.rs:4:44
|
LL | fn bug<T>() -> impl Iterator<Item = [(); { |x: [u8]| x }]> {
| ^^^^^^^^^^^ expected `usize`, found closure
|
= note: expected type `usize`
found closure `[closure@$DIR/ice-6251.rs:4:44: 4:55]`
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0277, E0308, E0601.
For more information about an error, try `rustc --explain E0277`.

View file

@ -0,0 +1,15 @@
// originally from glacier fixed/77919.rs
// encountered errors resolving bounds after type-checking
trait TypeVal<T> {
const VAL: T;
}
struct Five;
struct Multiply<N, M> {
_n: PhantomData,
}
impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
fn main() {
[1; <Multiply<Five, Five>>::VAL];
}

View file

@ -0,0 +1,46 @@
error[E0412]: cannot find type `PhantomData` in this scope
--> $DIR/ice-6252.rs:9:9
|
LL | _n: PhantomData,
| ^^^^^^^^^^^ not found in this scope
|
help: consider importing this struct
|
LL | use std::marker::PhantomData;
|
error[E0412]: cannot find type `VAL` in this scope
--> $DIR/ice-6252.rs:11:63
|
LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
| - ^^^ not found in this scope
| |
| help: you might be missing a type parameter: `, VAL`
error[E0046]: not all trait items implemented, missing: `VAL`
--> $DIR/ice-6252.rs:11:1
|
LL | const VAL: T;
| ------------- `VAL` from trait
...
LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation
error: any use of this value will cause an error
--> $DIR/ice-6252.rs:5:5
|
LL | const VAL: T;
| ^^^^^^^^^^^^^ no MIR body is available for DefId(0:5 ~ ice_6252[317d]::TypeVal::VAL)
|
= note: `#[deny(const_err)]` on by default
error[E0080]: evaluation of constant value failed
--> $DIR/ice-6252.rs:14:9
|
LL | [1; <Multiply<Five, Five>>::VAL];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
error: aborting due to 5 previous errors
Some errors have detailed explanations: E0046, E0080, E0412.
For more information about an error, try `rustc --explain E0046`.

View file

@ -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(_) => {},
}
}

View file

@ -0,0 +1,12 @@
error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
--> $DIR/ice-6254.rs:12:9
|
LL | FOO_REF_REF => {},
| ^^^^^^^^^^^
|
= note: `-D indirect-structural-match` implied by `-D warnings`
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #62411 <https://github.com/rust-lang/rust/issues/62411>
error: aborting due to previous error

View file

@ -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!();

View file

@ -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

View file

@ -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
}

View file

@ -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`.

View file

@ -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();
}

View file

@ -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

View file

@ -3,9 +3,6 @@
fn main() { fn main() {
let a = ["1", "lol", "3", "NaN", "5"]; let a = ["1", "lol", "3", "NaN", "5"];
let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
assert_eq!(element, Some(1));
#[rustfmt::skip] #[rustfmt::skip]
let _: Option<u32> = vec![1, 2, 3, 4, 5, 6] let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
.into_iter() .into_iter()

View file

@ -1,14 +1,5 @@
error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead. error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead.
--> $DIR/filter_map_next.rs:6:32 --> $DIR/filter_map_next.rs:7:26
|
LL | let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::filter-map-next` implied by `-D warnings`
= note: replace `filter_map(|s| s.parse().ok()).next()` with `find_map(|s| s.parse().ok())`
error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead.
--> $DIR/filter_map_next.rs:10:26
| |
LL | let _: Option<u32> = vec![1, 2, 3, 4, 5, 6] LL | let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
| __________________________^ | __________________________^
@ -19,6 +10,8 @@ LL | | if x == 2 {
LL | | }) LL | | })
LL | | .next(); 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

View file

@ -0,0 +1,10 @@
// run-rustfix
#![warn(clippy::all, clippy::pedantic)]
fn main() {
let a = ["1", "lol", "3", "NaN", "5"];
let element: Option<i32> = a.iter().find_map(|s| s.parse().ok());
assert_eq!(element, Some(1));
}

View file

@ -0,0 +1,10 @@
// run-rustfix
#![warn(clippy::all, clippy::pedantic)]
fn main() {
let a = ["1", "lol", "3", "NaN", "5"];
let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
assert_eq!(element, Some(1));
}

View file

@ -0,0 +1,10 @@
error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead.
--> $DIR/filter_map_next_fixable.rs:8:32
|
LL | let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())`
|
= note: `-D clippy::filter-map-next` implied by `-D warnings`
error: aborting due to previous error

View file

@ -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 --> $DIR/filter_methods.rs:5:21
| |
LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); 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` = note: `-D clippy::filter-map` implied by `-D warnings`
= help: this is more succinctly expressed by calling `.filter_map(..)` instead = 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 --> $DIR/filter_methods.rs:7:21
| |
LL | let _: Vec<_> = vec![5_i8; 6] 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()` = 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 --> $DIR/filter_methods.rs:13:21
| |
LL | let _: Vec<_> = vec![5_i8; 6] 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()` = 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 --> $DIR/filter_methods.rs:19:21
| |
LL | let _: Vec<_> = vec![5_i8; 6] LL | let _: Vec<_> = vec![5_i8; 6]

View file

@ -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 --> $DIR/find_map.rs:20:26
| |
LL | let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap()); LL | let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap());
@ -7,7 +7,7 @@ LL | let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s
= note: `-D clippy::find-map` implied by `-D warnings` = note: `-D clippy::find-map` implied by `-D warnings`
= help: this is more succinctly expressed by calling `.find_map(..)` instead = 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 --> $DIR/find_map.rs:23:29
| |
LL | let _: Option<Flavor> = desserts_of_the_week LL | let _: Option<Flavor> = desserts_of_the_week

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