mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 23:04:33 +00:00
Merge commit 'b20d4c155d2fe3a8391f86dcf9a8c49e17188703' into clippyup
This commit is contained in:
parent
0c1531a5c4
commit
34244190d4
6
.github/driver.sh
vendored
6
.github/driver.sh
vendored
@ -24,16 +24,16 @@ unset CARGO_MANIFEST_DIR
|
||||
# FIXME: How to match the clippy invocation in compile-test.rs?
|
||||
./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2>double_neg.stderr && exit 1
|
||||
sed -e "s,tests/ui,\$DIR," -e "/= help/d" double_neg.stderr >normalized.stderr
|
||||
diff normalized.stderr tests/ui/double_neg.stderr
|
||||
diff -u normalized.stderr tests/ui/double_neg.stderr
|
||||
|
||||
# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same
|
||||
SYSROOT=$(rustc --print sysroot)
|
||||
diff <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose)
|
||||
diff -u <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose)
|
||||
|
||||
echo "fn main() {}" >target/driver_test.rs
|
||||
# we can't run 2 rustcs on the same file at the same time
|
||||
CLIPPY=$(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc)
|
||||
RUSTC=$(rustc ./target/driver_test.rs)
|
||||
diff <($CLIPPY) <($RUSTC)
|
||||
diff -u <($CLIPPY) <($RUSTC)
|
||||
|
||||
# TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR
|
||||
|
10
CHANGELOG.md
10
CHANGELOG.md
@ -22,7 +22,7 @@ Current beta, release 2020-11-19
|
||||
* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
|
||||
* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
|
||||
* [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
|
||||
* [`single_char_push_str`] [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
|
||||
* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
|
||||
|
||||
### Moves and Deprecations
|
||||
|
||||
@ -1665,6 +1665,7 @@ Released 2018-09-13
|
||||
[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
|
||||
[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
|
||||
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
|
||||
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
|
||||
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
|
||||
[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
|
||||
[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
|
||||
@ -1713,6 +1714,7 @@ Released 2018-09-13
|
||||
[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
|
||||
[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
|
||||
[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
|
||||
[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
|
||||
[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
|
||||
[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
|
||||
[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
|
||||
@ -1731,6 +1733,7 @@ Released 2018-09-13
|
||||
[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
|
||||
[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
|
||||
[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
|
||||
[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
|
||||
[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
|
||||
[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
|
||||
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
|
||||
@ -1795,6 +1798,7 @@ Released 2018-09-13
|
||||
[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
|
||||
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
|
||||
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
|
||||
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
|
||||
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
|
||||
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
|
||||
[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
|
||||
@ -1802,6 +1806,7 @@ Released 2018-09-13
|
||||
[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
|
||||
[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
|
||||
[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
|
||||
[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
|
||||
[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
|
||||
[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
|
||||
[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
|
||||
@ -1917,6 +1922,7 @@ Released 2018-09-13
|
||||
[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
|
||||
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
|
||||
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
|
||||
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
|
||||
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
|
||||
[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
|
||||
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
|
||||
@ -1937,8 +1943,8 @@ Released 2018-09-13
|
||||
[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
|
||||
[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
|
||||
[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
|
||||
[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
|
||||
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
|
||||
[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str
|
||||
[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
|
||||
[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
|
||||
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
|
||||
|
@ -63,9 +63,10 @@ To figure out how this syntax structure is encoded in the AST, it is recommended
|
||||
Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting].
|
||||
But we can make it nest-less by using [if_chain] macro, [like this][nest-less].
|
||||
|
||||
[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an E-easy issue first.
|
||||
They are mostly classified as [`E-medium`], since they might be somewhat involved code wise,
|
||||
but not difficult per-se.
|
||||
[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`]
|
||||
first. Sometimes they are only somewhat involved code wise, but not difficult per-se.
|
||||
Note that [`E-medium`] issues may require some knowledge of Clippy internals or some
|
||||
debugging to find the actual problem behind the issue.
|
||||
|
||||
[`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
|
||||
lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
|
||||
|
15
README.md
15
README.md
@ -167,18 +167,21 @@ You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
|
||||
|
||||
* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
|
||||
|
||||
Note: `deny` produces errors instead of warnings.
|
||||
Note: `allow` means to suppress the lint for your code. With `warn` the lint
|
||||
will only emit a warning, while with `deny` the lint will emit an error, when
|
||||
triggering for your code. An error causes clippy to exit with an error code, so
|
||||
is useful in scripts like CI/CD.
|
||||
|
||||
If you do not want to include your lint levels in your code, you can globally enable/disable lints
|
||||
by passing extra flags to Clippy during the run:
|
||||
If you do not want to include your lint levels in your code, you can globally
|
||||
enable/disable lints by passing extra flags to Clippy during the run:
|
||||
|
||||
To disable `lint_name`, run
|
||||
To allow `lint_name`, run
|
||||
|
||||
```terminal
|
||||
cargo clippy -- -A clippy::lint_name
|
||||
```
|
||||
|
||||
And to enable `lint_name`, run
|
||||
And to warn on `lint_name`, run
|
||||
|
||||
```terminal
|
||||
cargo clippy -- -W clippy::lint_name
|
||||
@ -190,7 +193,7 @@ can run Clippy with warnings for all lints enabled:
|
||||
cargo clippy -- -W clippy::pedantic
|
||||
```
|
||||
|
||||
If you care only about a single lint, you can allow all others and then explicitly reenable
|
||||
If you care only about a single lint, you can allow all others and then explicitly warn on
|
||||
the lint(s) you are interested in:
|
||||
```terminal
|
||||
cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
|
||||
|
@ -11,7 +11,7 @@ use std::path::PathBuf;
|
||||
// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details
|
||||
|
||||
pub fn run(rustc_path: Option<&str>) {
|
||||
// we can unwrap here because the arg is required here
|
||||
// we can unwrap here because the arg is required by clap
|
||||
let rustc_path = PathBuf::from(rustc_path.unwrap());
|
||||
assert!(rustc_path.is_dir(), "path is not a directory");
|
||||
let rustc_source_basedir = rustc_path.join("compiler");
|
||||
@ -49,6 +49,15 @@ fn inject_deps_into_manifest(
|
||||
cargo_toml: &str,
|
||||
lib_rs: &str,
|
||||
) -> std::io::Result<()> {
|
||||
// do not inject deps if we have aleady done so
|
||||
if cargo_toml.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") {
|
||||
eprintln!(
|
||||
"cargo dev ra-setup: warning: deps already found inside {}, doing nothing.",
|
||||
manifest_path
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let extern_crates = lib_rs
|
||||
.lines()
|
||||
// get the deps
|
||||
|
@ -88,9 +88,28 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic {
|
||||
|
||||
let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r));
|
||||
if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_span = Some(expr.span);
|
||||
} else if l_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
|
||||
match op.node {
|
||||
hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind {
|
||||
hir::ExprKind::Lit(_lit) => (),
|
||||
hir::ExprKind::Unary(hir::UnOp::UnNeg, expr) => {
|
||||
if let hir::ExprKind::Lit(lit) = &expr.kind {
|
||||
if let rustc_ast::ast::LitKind::Int(1, _) = lit.node {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_span = Some(expr.span);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_span = Some(expr.span);
|
||||
},
|
||||
},
|
||||
_ => {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_span = Some(expr.span);
|
||||
},
|
||||
}
|
||||
} else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
|
||||
span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
|
||||
self.expr_span = Some(expr.span);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::{Symbol, SymbolStr};
|
||||
use semver::Version;
|
||||
|
||||
@ -286,14 +287,14 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
if items.is_empty() || !attr.has_name(sym!(deprecated)) {
|
||||
if items.is_empty() || !attr.has_name(sym::deprecated) {
|
||||
return;
|
||||
}
|
||||
for item in items {
|
||||
if_chain! {
|
||||
if let NestedMetaItem::MetaItem(mi) = &item;
|
||||
if let MetaItemKind::NameValue(lit) = &mi.kind;
|
||||
if mi.has_name(sym!(since));
|
||||
if mi.has_name(sym::since);
|
||||
then {
|
||||
check_semver(cx, item.span(), lit);
|
||||
}
|
||||
@ -309,7 +310,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
||||
}
|
||||
match item.kind {
|
||||
ItemKind::ExternCrate(..) | ItemKind::Use(..) => {
|
||||
let skip_unused_imports = item.attrs.iter().any(|attr| attr.has_name(sym!(macro_use)));
|
||||
let skip_unused_imports = item.attrs.iter().any(|attr| attr.has_name(sym::macro_use));
|
||||
|
||||
for attr in item.attrs {
|
||||
if in_external_macro(cx.sess(), attr.span) {
|
||||
@ -326,7 +327,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
||||
match item.kind {
|
||||
ItemKind::Use(..) => {
|
||||
if is_word(lint, sym!(unused_imports))
|
||||
|| is_word(lint, sym!(deprecated))
|
||||
|| is_word(lint, sym::deprecated)
|
||||
|| is_word(lint, sym!(unreachable_pub))
|
||||
|| is_word(lint, sym!(unused))
|
||||
|| extract_clippy_lint(lint)
|
||||
@ -411,8 +412,7 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMet
|
||||
let lint_store = cx.lints();
|
||||
for lint in items {
|
||||
if let Some(lint_name) = extract_clippy_lint(lint) {
|
||||
if let CheckLintNameResult::Tool(Err((None, _))) =
|
||||
lint_store.check_lint_name(&lint_name, Some(sym!(clippy)))
|
||||
if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(&lint_name, Some(sym::clippy))
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
@ -529,10 +529,10 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut
|
||||
|
||||
for attr in attrs {
|
||||
if let Some(values) = attr.meta_item_list() {
|
||||
if values.len() != 1 || !attr.has_name(sym!(inline)) {
|
||||
if values.len() != 1 || !attr.has_name(sym::inline) {
|
||||
continue;
|
||||
}
|
||||
if is_word(&values[0], sym!(always)) {
|
||||
if is_word(&values[0], sym::always) {
|
||||
span_lint(
|
||||
cx,
|
||||
INLINE_ALWAYS,
|
||||
@ -623,12 +623,12 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It
|
||||
fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
if_chain! {
|
||||
// check cfg_attr
|
||||
if attr.has_name(sym!(cfg_attr));
|
||||
if attr.has_name(sym::cfg_attr);
|
||||
if let Some(items) = attr.meta_item_list();
|
||||
if items.len() == 2;
|
||||
// check for `rustfmt`
|
||||
if let Some(feature_item) = items[0].meta_item();
|
||||
if feature_item.has_name(sym!(rustfmt));
|
||||
if feature_item.has_name(sym::rustfmt);
|
||||
// check for `rustfmt_skip` and `rustfmt::skip`
|
||||
if let Some(skip_item) = &items[1].meta_item();
|
||||
if skip_item.has_name(sym!(rustfmt_skip)) ||
|
||||
@ -690,7 +690,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if attr.has_name(sym!(cfg));
|
||||
if attr.has_name(sym::cfg);
|
||||
if let Some(list) = attr.meta_item_list();
|
||||
let mismatched = find_mismatched_target_os(&list);
|
||||
if !mismatched.is_empty();
|
||||
|
@ -11,6 +11,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for boolean expressions that can be written more
|
||||
@ -253,8 +254,8 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
|
||||
},
|
||||
ExprKind::MethodCall(path, _, args, _) if args.len() == 1 => {
|
||||
let type_of_receiver = cx.typeck_results().expr_ty(&args[0]);
|
||||
if !is_type_diagnostic_item(cx, type_of_receiver, sym!(option_type))
|
||||
&& !is_type_diagnostic_item(cx, type_of_receiver, sym!(result_type))
|
||||
if !is_type_diagnostic_item(cx, type_of_receiver, sym::option_type)
|
||||
&& !is_type_diagnostic_item(cx, type_of_receiver, sym::result_type)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::Symbol;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
|
||||
let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) =
|
||||
filter_args[0].kind {
|
||||
let p = path.ident.name;
|
||||
if (p == sym!(iter) || p == sym!(iter_mut)) && args.len() == 1 {
|
||||
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
|
||||
&args[0]
|
||||
} else {
|
||||
&filter_args[0]
|
||||
|
@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::BytePos;
|
||||
use rustc_span::{sym, BytePos};
|
||||
|
||||
use crate::utils::{is_type_diagnostic_item, snippet_opt, span_lint_and_help, LimitStack};
|
||||
|
||||
@ -61,7 +61,7 @@ impl CognitiveComplexity {
|
||||
helper.visit_expr(expr);
|
||||
let CCHelper { cc, returns } = helper;
|
||||
let ret_ty = cx.typeck_results().node_type(expr.hir_id);
|
||||
let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym!(result_type)) {
|
||||
let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::result_type) {
|
||||
returns
|
||||
} else {
|
||||
#[allow(clippy::integer_division)]
|
||||
@ -123,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity {
|
||||
hir_id: HirId,
|
||||
) {
|
||||
let def_id = cx.tcx.hir().local_def_id(hir_id);
|
||||
if !cx.tcx.has_attr(def_id.to_def_id(), sym!(test)) {
|
||||
if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) {
|
||||
self.check(cx, kind, decl, body, span);
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ use rustc_data_structures::sync::Lrc;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::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::ty::subst::{Subst, SubstsRef};
|
||||
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use std::cmp::Ordering::{self, Equal};
|
||||
use std::convert::TryInto;
|
||||
@ -501,7 +501,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||
}
|
||||
|
||||
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 {
|
||||
ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => {
|
||||
match result.ty.kind() {
|
||||
|
304
clippy_lints/src/default.rs
Normal file
304
clippy_lints/src/default.rs
Normal 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![]
|
||||
}
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ use rustc_parse::maybe_new_parser_from_source_str;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span};
|
||||
use rustc_span::{FileName, Pos};
|
||||
use rustc_span::{sym, FileName, Pos};
|
||||
use std::io;
|
||||
use std::ops::Range;
|
||||
use url::Url;
|
||||
@ -237,7 +237,7 @@ fn lint_for_missing_headers<'tcx>(
|
||||
);
|
||||
}
|
||||
if !headers.errors {
|
||||
if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) {
|
||||
if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) {
|
||||
span_lint(
|
||||
cx,
|
||||
MISSING_ERRORS_DOC,
|
||||
@ -255,7 +255,7 @@ fn lint_for_missing_headers<'tcx>(
|
||||
if let ty::Opaque(_, subs) = ret_ty.kind();
|
||||
if let Some(gen) = subs.types().next();
|
||||
if let ty::Generator(_, subs, _) = gen.kind();
|
||||
if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym!(result_type));
|
||||
if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::result_type);
|
||||
then {
|
||||
span_lint(
|
||||
cx,
|
||||
@ -333,7 +333,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
|
||||
let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span);
|
||||
spans.extend_from_slice(¤t_spans);
|
||||
doc.push_str(&comment);
|
||||
} else if attr.has_name(sym!(doc)) {
|
||||
} else if attr.has_name(sym::doc) {
|
||||
// ignore mix of sugared and non-sugared doc
|
||||
// don't trigger the safety or errors check
|
||||
return DocHeaders {
|
||||
@ -479,7 +479,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, span: Span) {
|
||||
| ItemKind::ExternCrate(..)
|
||||
| ItemKind::ForeignMod(..) => return false,
|
||||
// We found a main function ...
|
||||
ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym!(main) => {
|
||||
ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => {
|
||||
let is_async = matches!(sig.header.asyncness, Async::Yes{..});
|
||||
let returns_nothing = match &sig.decl.output {
|
||||
FnRetTy::Default(..) => true,
|
||||
|
@ -5,6 +5,7 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be
|
||||
@ -33,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
||||
if_chain! {
|
||||
// match call to unwrap
|
||||
if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args, _) = expr.kind;
|
||||
if unwrap_fun.ident.name == sym!(unwrap);
|
||||
if unwrap_fun.ident.name == sym::unwrap;
|
||||
// match call to write_fmt
|
||||
if !unwrap_args.is_empty();
|
||||
if let ExprKind::MethodCall(ref write_fun, _, write_args, _) =
|
||||
|
@ -6,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
|
||||
@ -95,8 +95,8 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h
|
||||
// check for `unwrap`
|
||||
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
|
||||
let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
|
||||
if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
|
||||
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
|
||||
if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type)
|
||||
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type)
|
||||
{
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
@ -113,7 +113,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h
|
||||
|
||||
for impl_item in impl_items {
|
||||
if_chain! {
|
||||
if impl_item.ident.name == sym!(from);
|
||||
if impl_item.ident.name == sym::from;
|
||||
if let ImplItemKind::Fn(_, body_id) =
|
||||
cx.tcx.hir().impl_item(impl_item.id).kind;
|
||||
then {
|
||||
|
@ -10,6 +10,7 @@ use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, MatchSource, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for the use of `format!("string literal with no
|
||||
@ -91,7 +92,7 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &
|
||||
if pats.len() == 1;
|
||||
then {
|
||||
let ty = cx.typeck_results().pat_ty(&pats[0]).peel_refs();
|
||||
if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) {
|
||||
if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym::string_type) {
|
||||
return None;
|
||||
}
|
||||
if let ExprKind::Lit(ref lit) = format_args.kind {
|
||||
@ -186,15 +187,15 @@ fn check_unformatted(expr: &Expr<'_>) -> bool {
|
||||
if exprs.len() == 1;
|
||||
// struct `core::fmt::rt::v1::Argument`
|
||||
if let ExprKind::Struct(_, ref fields, _) = exprs[0].kind;
|
||||
if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym!(format));
|
||||
if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
|
||||
// struct `core::fmt::rt::v1::FormatSpec`
|
||||
if let ExprKind::Struct(_, ref fields, _) = format_field.expr.kind;
|
||||
if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym!(precision));
|
||||
if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym::precision);
|
||||
if let ExprKind::Path(ref precision_path) = precision_field.expr.kind;
|
||||
if last_path_segment(precision_path).ident.name == sym!(Implied);
|
||||
if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym!(width));
|
||||
if last_path_segment(precision_path).ident.name == sym::Implied;
|
||||
if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym::width);
|
||||
if let ExprKind::Path(ref width_qpath) = width_field.expr.kind;
|
||||
if last_path_segment(width_qpath).ident.name == sym!(Implied);
|
||||
if last_path_segment(width_qpath).ident.name == sym::Implied;
|
||||
then {
|
||||
return true;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
@ -473,7 +474,7 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span
|
||||
if !in_external_macro(cx.sess(), item_span);
|
||||
if let hir::FnRetTy::Return(ref ty) = decl.output;
|
||||
if let hir::TyKind::Path(ref qpath) = ty.kind;
|
||||
if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym!(result_type));
|
||||
if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym::result_type);
|
||||
if let Some(ref args) = last_path_segment(qpath).args;
|
||||
if let [_, hir::GenericArg::Type(ref err_ty)] = args.args;
|
||||
if let hir::TyKind::Tup(t) = err_ty.kind;
|
||||
|
@ -8,6 +8,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for using `x.get(x.len() - 1)` instead of
|
||||
@ -55,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for GetLastWithLen {
|
||||
// Argument 0 (the struct we're calling the method on) is a vector
|
||||
if let Some(struct_calling_on) = args.get(0);
|
||||
let struct_ty = cx.typeck_results().expr_ty(struct_calling_on);
|
||||
if is_type_diagnostic_item(cx, struct_ty, sym!(vec_type));
|
||||
if is_type_diagnostic_item(cx, struct_ty, sym::vec_type);
|
||||
|
||||
// Argument to "get" is a subtraction
|
||||
if let Some(get_index_arg) = args.get(1);
|
||||
|
@ -4,6 +4,7 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:*** Checks for unnecessary `ok()` in if let.
|
||||
@ -45,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet {
|
||||
if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = op.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
|
||||
if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation
|
||||
if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym!(result_type));
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type);
|
||||
if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
|
||||
|
||||
then {
|
||||
|
@ -2,6 +2,7 @@ use if_chain::if_chain;
|
||||
use rustc_hir::{ImplItem, ImplItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
use crate::utils::{
|
||||
get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help,
|
||||
@ -107,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString {
|
||||
if decl.inputs.len() == 1;
|
||||
|
||||
// Check if return type is String
|
||||
if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type));
|
||||
if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::string_type);
|
||||
|
||||
// Filters instances of to_string which are required by a trait
|
||||
if trait_ref_of_method(cx, impl_item.hir_id).is_none();
|
||||
|
@ -7,7 +7,7 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::{TraitFn, TraitItem, TraitItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Symbol;
|
||||
use rustc_span::{sym, Symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `#[inline]` on trait methods without bodies
|
||||
@ -41,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody {
|
||||
|
||||
fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) {
|
||||
for attr in attrs {
|
||||
if !attr.has_name(sym!(inline)) {
|
||||
if !attr.has_name(sym::inline) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
use crate::utils::span_lint;
|
||||
use rustc_ast::ast::{Block, ItemKind, StmtKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -53,7 +54,7 @@ declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]);
|
||||
|
||||
impl EarlyLintPass for ItemsAfterStatements {
|
||||
fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) {
|
||||
if item.span.from_expansion() {
|
||||
if in_external_macro(cx.sess(), item.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -67,7 +68,7 @@ impl EarlyLintPass for ItemsAfterStatements {
|
||||
// lint on all further items
|
||||
for stmt in stmts {
|
||||
if let StmtKind::Item(ref it) = *stmt {
|
||||
if it.span.from_expansion() {
|
||||
if in_external_macro(cx.sess(), it.span) {
|
||||
return;
|
||||
}
|
||||
if let ItemKind::MacroDef(..) = it.kind {
|
||||
|
@ -68,7 +68,44 @@ declare_clippy_lint! {
|
||||
"traits or impls with a public `len` method but no corresponding `is_empty` method"
|
||||
}
|
||||
|
||||
declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY]);
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for comparing to an empty slice such as "" or [],`
|
||||
/// and suggests using `.is_empty()` where applicable.
|
||||
///
|
||||
/// **Why is this bad?** Some structures can answer `.is_empty()` much faster
|
||||
/// than checking for equality. So it is good to get into the habit of using
|
||||
/// `.is_empty()`, and having it is cheap.
|
||||
/// Besides, it makes the intent clearer than a manual comparison in some contexts.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```ignore
|
||||
/// if s == "" {
|
||||
/// ..
|
||||
/// }
|
||||
///
|
||||
/// if arr == [] {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```ignore
|
||||
/// if s.is_empty() {
|
||||
/// ..
|
||||
/// }
|
||||
///
|
||||
/// if arr.is_empty() {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
pub COMPARISON_TO_EMPTY,
|
||||
style,
|
||||
"checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
|
||||
}
|
||||
|
||||
declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LenZero {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
@ -221,6 +258,8 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>
|
||||
}
|
||||
|
||||
check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to)
|
||||
} else {
|
||||
check_empty_expr(cx, span, method, lit, op)
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,6 +297,42 @@ fn check_len(
|
||||
}
|
||||
}
|
||||
|
||||
fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) {
|
||||
if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
COMPARISON_TO_EMPTY,
|
||||
span,
|
||||
"comparison to empty slice",
|
||||
&format!("using `{}is_empty` is clearer and more explicit", op),
|
||||
format!(
|
||||
"{}{}.is_empty()",
|
||||
op,
|
||||
snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty_string(expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Lit(ref lit) = expr.kind {
|
||||
if let LitKind::Str(lit, _) = lit.node {
|
||||
let lit = lit.as_str();
|
||||
return lit == "";
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_empty_array(expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Array(ref arr) = expr.kind {
|
||||
return arr.is_empty();
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks if this type has an `is_empty` method.
|
||||
fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
/// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
|
||||
|
@ -176,7 +176,7 @@ mod copies;
|
||||
mod copy_iterator;
|
||||
mod create_dir;
|
||||
mod dbg_macro;
|
||||
mod default_trait_access;
|
||||
mod default;
|
||||
mod dereference;
|
||||
mod derive;
|
||||
mod disallowed_method;
|
||||
@ -234,6 +234,7 @@ mod macro_use;
|
||||
mod main_recursion;
|
||||
mod manual_async_fn;
|
||||
mod manual_non_exhaustive;
|
||||
mod manual_ok_or;
|
||||
mod manual_strip;
|
||||
mod manual_unwrap_or;
|
||||
mod map_clone;
|
||||
@ -294,6 +295,7 @@ mod redundant_closure_call;
|
||||
mod redundant_field_names;
|
||||
mod redundant_pub_crate;
|
||||
mod redundant_static_lifetimes;
|
||||
mod ref_option_ref;
|
||||
mod reference;
|
||||
mod regex;
|
||||
mod repeat_once;
|
||||
@ -537,7 +539,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
©_iterator::COPY_ITERATOR,
|
||||
&create_dir::CREATE_DIR,
|
||||
&dbg_macro::DBG_MACRO,
|
||||
&default_trait_access::DEFAULT_TRAIT_ACCESS,
|
||||
&default::DEFAULT_TRAIT_ACCESS,
|
||||
&default::FIELD_REASSIGN_WITH_DEFAULT,
|
||||
&dereference::EXPLICIT_DEREF_METHODS,
|
||||
&derive::DERIVE_HASH_XOR_EQ,
|
||||
&derive::DERIVE_ORD_XOR_PARTIAL_ORD,
|
||||
@ -615,6 +618,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&large_const_arrays::LARGE_CONST_ARRAYS,
|
||||
&large_enum_variant::LARGE_ENUM_VARIANT,
|
||||
&large_stack_arrays::LARGE_STACK_ARRAYS,
|
||||
&len_zero::COMPARISON_TO_EMPTY,
|
||||
&len_zero::LEN_WITHOUT_IS_EMPTY,
|
||||
&len_zero::LEN_ZERO,
|
||||
&let_if_seq::USELESS_LET_IF_SEQ,
|
||||
@ -649,6 +653,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&main_recursion::MAIN_RECURSION,
|
||||
&manual_async_fn::MANUAL_ASYNC_FN,
|
||||
&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
|
||||
&manual_ok_or::MANUAL_OK_OR,
|
||||
&manual_strip::MANUAL_STRIP,
|
||||
&manual_unwrap_or::MANUAL_UNWRAP_OR,
|
||||
&map_clone::MAP_CLONE,
|
||||
@ -692,6 +697,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&methods::FILTER_NEXT,
|
||||
&methods::FIND_MAP,
|
||||
&methods::FLAT_MAP_IDENTITY,
|
||||
&methods::FROM_ITER_INSTEAD_OF_COLLECT,
|
||||
&methods::GET_UNWRAP,
|
||||
&methods::INEFFICIENT_TO_STRING,
|
||||
&methods::INTO_ITER_ON_REF,
|
||||
@ -702,6 +708,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&methods::ITER_NTH_ZERO,
|
||||
&methods::ITER_SKIP_NEXT,
|
||||
&methods::MANUAL_SATURATING_ARITHMETIC,
|
||||
&methods::MAP_COLLECT_RESULT_UNIT,
|
||||
&methods::MAP_FLATTEN,
|
||||
&methods::MAP_UNWRAP_OR,
|
||||
&methods::NEW_RET_NO_SELF,
|
||||
@ -712,8 +719,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&methods::RESULT_MAP_OR_INTO_OPTION,
|
||||
&methods::SEARCH_IS_SOME,
|
||||
&methods::SHOULD_IMPLEMENT_TRAIT,
|
||||
&methods::SINGLE_CHAR_ADD_STR,
|
||||
&methods::SINGLE_CHAR_PATTERN,
|
||||
&methods::SINGLE_CHAR_PUSH_STR,
|
||||
&methods::SKIP_WHILE_NEXT,
|
||||
&methods::STRING_EXTEND_CHARS,
|
||||
&methods::SUSPICIOUS_MAP,
|
||||
@ -807,6 +814,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&redundant_field_names::REDUNDANT_FIELD_NAMES,
|
||||
&redundant_pub_crate::REDUNDANT_PUB_CRATE,
|
||||
&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
|
||||
&ref_option_ref::REF_OPTION_REF,
|
||||
&reference::DEREF_ADDROF,
|
||||
&reference::REF_IN_DEREF,
|
||||
®ex::INVALID_REGEX,
|
||||
@ -1030,6 +1038,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&sess.target,
|
||||
);
|
||||
store.register_late_pass(move || box pass_by_ref_or_value);
|
||||
store.register_late_pass(|| box ref_option_ref::RefOptionRef);
|
||||
store.register_late_pass(|| box try_err::TryErr);
|
||||
store.register_late_pass(|| box use_self::UseSelf);
|
||||
store.register_late_pass(|| box bytecount::ByteCount);
|
||||
@ -1047,7 +1056,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd);
|
||||
store.register_late_pass(|| box unwrap::Unwrap);
|
||||
store.register_late_pass(|| box duration_subsec::DurationSubsec);
|
||||
store.register_late_pass(|| box default_trait_access::DefaultTraitAccess);
|
||||
store.register_late_pass(|| box indexing_slicing::IndexingSlicing);
|
||||
store.register_late_pass(|| box non_copy_const::NonCopyConst);
|
||||
store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast);
|
||||
@ -1098,6 +1106,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
let enum_variant_name_threshold = conf.enum_variant_name_threshold;
|
||||
store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
|
||||
store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
|
||||
store.register_late_pass(|| box default::Default::default());
|
||||
store.register_late_pass(|| box unused_self::UnusedSelf);
|
||||
store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
|
||||
store.register_late_pass(|| box exit::Exit);
|
||||
@ -1146,6 +1155,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box unwrap_in_result::UnwrapInResult);
|
||||
store.register_late_pass(|| box self_assignment::SelfAssignment);
|
||||
store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr);
|
||||
store.register_late_pass(|| box manual_ok_or::ManualOkOr);
|
||||
store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
|
||||
store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
|
||||
store.register_late_pass(|| box manual_strip::ManualStrip);
|
||||
@ -1210,7 +1220,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
|
||||
LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
|
||||
LintId::of(©_iterator::COPY_ITERATOR),
|
||||
LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS),
|
||||
LintId::of(&default::DEFAULT_TRAIT_ACCESS),
|
||||
LintId::of(&dereference::EXPLICIT_DEREF_METHODS),
|
||||
LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY),
|
||||
LintId::of(&derive::UNSAFE_DERIVE_DESERIALIZE),
|
||||
@ -1234,6 +1244,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP),
|
||||
LintId::of(&loops::EXPLICIT_ITER_LOOP),
|
||||
LintId::of(¯o_use::MACRO_USE_IMPORTS),
|
||||
LintId::of(&manual_ok_or::MANUAL_OK_OR),
|
||||
LintId::of(&map_err_ignore::MAP_ERR_IGNORE),
|
||||
LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
|
||||
LintId::of(&matches::MATCH_BOOL),
|
||||
@ -1258,6 +1269,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
|
||||
LintId::of(&ranges::RANGE_MINUS_ONE),
|
||||
LintId::of(&ranges::RANGE_PLUS_ONE),
|
||||
LintId::of(&ref_option_ref::REF_OPTION_REF),
|
||||
LintId::of(&shadow::SHADOW_UNRELATED),
|
||||
LintId::of(&strings::STRING_ADD_ASSIGN),
|
||||
LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
|
||||
@ -1319,6 +1331,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&comparison_chain::COMPARISON_CHAIN),
|
||||
LintId::of(&copies::IFS_SAME_COND),
|
||||
LintId::of(&copies::IF_SAME_THEN_ELSE),
|
||||
LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT),
|
||||
LintId::of(&derive::DERIVE_HASH_XOR_EQ),
|
||||
LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD),
|
||||
LintId::of(&doc::MISSING_SAFETY_DOC),
|
||||
@ -1366,6 +1379,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&int_plus_one::INT_PLUS_ONE),
|
||||
LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS),
|
||||
LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
|
||||
LintId::of(&len_zero::COMPARISON_TO_EMPTY),
|
||||
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
|
||||
LintId::of(&len_zero::LEN_ZERO),
|
||||
LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
|
||||
@ -1419,6 +1433,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&methods::EXPECT_FUN_CALL),
|
||||
LintId::of(&methods::FILTER_NEXT),
|
||||
LintId::of(&methods::FLAT_MAP_IDENTITY),
|
||||
LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
|
||||
LintId::of(&methods::INTO_ITER_ON_REF),
|
||||
LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
|
||||
LintId::of(&methods::ITER_CLONED_COLLECT),
|
||||
@ -1427,6 +1442,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&methods::ITER_NTH_ZERO),
|
||||
LintId::of(&methods::ITER_SKIP_NEXT),
|
||||
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
LintId::of(&methods::MAP_COLLECT_RESULT_UNIT),
|
||||
LintId::of(&methods::NEW_RET_NO_SELF),
|
||||
LintId::of(&methods::OK_EXPECT),
|
||||
LintId::of(&methods::OPTION_AS_REF_DEREF),
|
||||
@ -1435,8 +1451,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION),
|
||||
LintId::of(&methods::SEARCH_IS_SOME),
|
||||
LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT),
|
||||
LintId::of(&methods::SINGLE_CHAR_ADD_STR),
|
||||
LintId::of(&methods::SINGLE_CHAR_PATTERN),
|
||||
LintId::of(&methods::SINGLE_CHAR_PUSH_STR),
|
||||
LintId::of(&methods::SKIP_WHILE_NEXT),
|
||||
LintId::of(&methods::STRING_EXTEND_CHARS),
|
||||
LintId::of(&methods::SUSPICIOUS_MAP),
|
||||
@ -1577,6 +1593,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||
LintId::of(&collapsible_if::COLLAPSIBLE_IF),
|
||||
LintId::of(&comparison_chain::COMPARISON_CHAIN),
|
||||
LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT),
|
||||
LintId::of(&doc::MISSING_SAFETY_DOC),
|
||||
LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),
|
||||
LintId::of(&enum_variants::ENUM_VARIANT_NAMES),
|
||||
@ -1592,6 +1609,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&functions::RESULT_UNIT_ERR),
|
||||
LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
|
||||
LintId::of(&inherent_to_string::INHERENT_TO_STRING),
|
||||
LintId::of(&len_zero::COMPARISON_TO_EMPTY),
|
||||
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
|
||||
LintId::of(&len_zero::LEN_ZERO),
|
||||
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
|
||||
@ -1615,18 +1633,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
|
||||
LintId::of(&methods::CHARS_LAST_CMP),
|
||||
LintId::of(&methods::CHARS_NEXT_CMP),
|
||||
LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
|
||||
LintId::of(&methods::INTO_ITER_ON_REF),
|
||||
LintId::of(&methods::ITER_CLONED_COLLECT),
|
||||
LintId::of(&methods::ITER_NEXT_SLICE),
|
||||
LintId::of(&methods::ITER_NTH_ZERO),
|
||||
LintId::of(&methods::ITER_SKIP_NEXT),
|
||||
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
LintId::of(&methods::MAP_COLLECT_RESULT_UNIT),
|
||||
LintId::of(&methods::NEW_RET_NO_SELF),
|
||||
LintId::of(&methods::OK_EXPECT),
|
||||
LintId::of(&methods::OPTION_MAP_OR_NONE),
|
||||
LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION),
|
||||
LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT),
|
||||
LintId::of(&methods::SINGLE_CHAR_PUSH_STR),
|
||||
LintId::of(&methods::SINGLE_CHAR_ADD_STR),
|
||||
LintId::of(&methods::STRING_EXTEND_CHARS),
|
||||
LintId::of(&methods::UNNECESSARY_FOLD),
|
||||
LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS),
|
||||
@ -1953,6 +1973,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
|
||||
ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles");
|
||||
ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
|
||||
ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
|
||||
ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
|
||||
}
|
||||
|
||||
// only exists to let the dogfood integration test works.
|
||||
|
@ -16,7 +16,6 @@ use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{kw, Symbol};
|
||||
use std::iter::FromIterator;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for lifetime annotations which can be removed by
|
||||
@ -214,14 +213,15 @@ fn could_use_elision<'tcx>(
|
||||
}
|
||||
|
||||
if allowed_lts
|
||||
.intersection(&FxHashSet::from_iter(
|
||||
input_visitor
|
||||
.intersection(
|
||||
&input_visitor
|
||||
.nested_elision_site_lts
|
||||
.iter()
|
||||
.chain(output_visitor.nested_elision_site_lts.iter())
|
||||
.cloned()
|
||||
.filter(|v| matches!(v, RefLt::Named(_))),
|
||||
))
|
||||
.filter(|v| matches!(v, RefLt::Named(_)))
|
||||
.collect(),
|
||||
)
|
||||
.next()
|
||||
.is_some()
|
||||
{
|
||||
|
@ -619,9 +619,9 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
|
||||
}
|
||||
|
||||
let lhs_constructor = last_path_segment(qpath);
|
||||
if method_path.ident.name == sym!(next)
|
||||
if method_path.ident.name == sym::next
|
||||
&& match_trait_method(cx, match_expr, &paths::ITERATOR)
|
||||
&& lhs_constructor.ident.name == sym!(Some)
|
||||
&& lhs_constructor.ident.name == sym::Some
|
||||
&& (pat_args.is_empty()
|
||||
|| !is_refutable(cx, &pat_args[0])
|
||||
&& !is_used_inside(cx, iter_expr, &arms[0].body)
|
||||
@ -985,13 +985,13 @@ fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool {
|
||||
_ => false,
|
||||
};
|
||||
|
||||
is_slice || is_type_diagnostic_item(cx, ty, sym!(vec_type)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type))
|
||||
is_slice || is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type))
|
||||
}
|
||||
|
||||
fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
|
||||
if method.ident.name == sym!(clone);
|
||||
if method.ident.name == sym::clone;
|
||||
if args.len() == 1;
|
||||
if let Some(arg) = args.get(0);
|
||||
then { arg } else { expr }
|
||||
@ -1355,7 +1355,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&
|
||||
if let Some(self_expr) = args.get(0);
|
||||
if let Some(pushed_item) = args.get(1);
|
||||
// Check that the method being called is push() on a Vec
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym!(vec_type));
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::vec_type);
|
||||
if path.ident.name.as_str() == "push";
|
||||
then {
|
||||
return Some((self_expr, pushed_item))
|
||||
@ -1736,7 +1736,7 @@ fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr:
|
||||
/// Checks for `for` loops over `Option`s and `Result`s.
|
||||
fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
|
||||
let ty = cx.typeck_results().expr_ty(arg);
|
||||
if is_type_diagnostic_item(cx, ty, sym!(option_type)) {
|
||||
if is_type_diagnostic_item(cx, ty, sym::option_type) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
FOR_LOOPS_OVER_FALLIBLES,
|
||||
@ -1753,7 +1753,7 @@ fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
|
||||
snippet(cx, arg.span, "_")
|
||||
),
|
||||
);
|
||||
} else if is_type_diagnostic_item(cx, ty, sym!(result_type)) {
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::result_type) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
FOR_LOOPS_OVER_FALLIBLES,
|
||||
@ -2186,8 +2186,8 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
|
||||
if_chain! {
|
||||
// a range index op
|
||||
if let ExprKind::MethodCall(ref meth, _, ref args, _) = expr.kind;
|
||||
if (meth.ident.name == sym!(index) && match_trait_method(self.cx, expr, &paths::INDEX))
|
||||
|| (meth.ident.name == sym!(index_mut) && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
|
||||
if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX))
|
||||
|| (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
|
||||
if !self.check(&args[1], &args[0], expr);
|
||||
then { return }
|
||||
}
|
||||
@ -2333,7 +2333,7 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
// will allow further borrows afterwards
|
||||
let ty = cx.typeck_results().expr_ty(e);
|
||||
is_iterable_array(ty, cx) ||
|
||||
is_type_diagnostic_item(cx, ty, sym!(vec_type)) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::vec_type) ||
|
||||
match_type(cx, ty, &paths::LINKED_LIST) ||
|
||||
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) ||
|
||||
is_type_diagnostic_item(cx, ty, sym!(hashset_type)) ||
|
||||
@ -2890,7 +2890,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
|
||||
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
|
||||
then {
|
||||
let ty = cx.typeck_results().node_type(ty.hir_id);
|
||||
if is_type_diagnostic_item(cx, ty, sym!(vec_type)) ||
|
||||
if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
|
||||
is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
|
||||
match_type(cx, ty, &paths::BTREEMAP) ||
|
||||
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) {
|
||||
|
@ -5,7 +5,7 @@ use rustc_attr as attr;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
|
||||
@ -83,9 +83,9 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants
|
||||
}
|
||||
|
||||
fn is_doc_hidden(attr: &Attribute) -> bool {
|
||||
attr.has_name(sym!(doc))
|
||||
attr.has_name(sym::doc)
|
||||
&& match attr.meta_item_list() {
|
||||
Some(l) => attr::list_contains_name(&l, sym!(hidden)),
|
||||
Some(l) => attr::list_contains_name(&l, sym::hidden),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
@ -102,7 +102,7 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants
|
||||
"this seems like a manual implementation of the non-exhaustive pattern",
|
||||
|diag| {
|
||||
if_chain! {
|
||||
if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive)));
|
||||
if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
|
||||
let header_span = cx.sess.source_map().span_until_char(item.span, '{');
|
||||
if let Some(snippet) = snippet_opt(cx, header_span);
|
||||
then {
|
||||
@ -154,7 +154,7 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data:
|
||||
"this seems like a manual implementation of the non-exhaustive pattern",
|
||||
|diag| {
|
||||
if_chain! {
|
||||
if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive)));
|
||||
if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
|
||||
let header_span = find_header_span(cx, item, data);
|
||||
if let Some(snippet) = snippet_opt(cx, header_span);
|
||||
then {
|
||||
|
99
clippy_lints/src/manual_ok_or.rs
Normal file
99
clippy_lints/src/manual_ok_or.rs
Normal 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 }
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ use rustc_lint::LintContext;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
@ -97,9 +98,9 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind;
|
||||
let ty = cx.typeck_results().expr_ty(scrutinee);
|
||||
if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)) {
|
||||
if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym::option_type) {
|
||||
Some(Case::Option)
|
||||
} else if utils::is_type_diagnostic_item(cx, ty, sym!(result_type)) {
|
||||
} else if utils::is_type_diagnostic_item(cx, ty, sym::result_type) {
|
||||
Some(Case::Result)
|
||||
} else {
|
||||
None
|
||||
|
@ -10,7 +10,7 @@ use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests
|
||||
@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone {
|
||||
if args.len() == 2;
|
||||
if method.ident.as_str() == "map";
|
||||
let ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
if is_type_diagnostic_item(cx, ty, sym!(option_type)) || match_trait_method(cx, e, &paths::ITERATOR);
|
||||
if is_type_diagnostic_item(cx, ty, sym::option_type) || match_trait_method(cx, e, &paths::ITERATOR);
|
||||
if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind;
|
||||
let closure_body = cx.tcx.hir().body(body_id);
|
||||
let closure_expr = remove_blocks(&closure_body.value);
|
||||
|
@ -7,6 +7,7 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
|
||||
@ -65,8 +66,8 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a
|
||||
if args.len() == 2 && method.ident.as_str() == "map";
|
||||
let caller_ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR)
|
||||
|| is_type_diagnostic_item(cx, caller_ty, sym!(result_type))
|
||||
|| is_type_diagnostic_item(cx, caller_ty, sym!(option_type));
|
||||
|| is_type_diagnostic_item(cx, caller_ty, sym::result_type)
|
||||
|| is_type_diagnostic_item(cx, caller_ty, sym::option_type);
|
||||
then {
|
||||
Some(args)
|
||||
} else {
|
||||
|
@ -6,6 +6,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `option.map(f)` where f is a function
|
||||
@ -206,9 +207,9 @@ fn lint_map_unit_fn(cx: &LateContext<'_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr
|
||||
let var_arg = &map_args[0];
|
||||
|
||||
let (map_type, variant, lint) =
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym!(option_type)) {
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::option_type) {
|
||||
("Option", "Some", OPTION_MAP_UNIT_FN)
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym!(result_type)) {
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::result_type) {
|
||||
("Result", "Ok", RESULT_MAP_UNIT_FN)
|
||||
} else {
|
||||
return;
|
||||
|
@ -5,6 +5,7 @@ use rustc_hir::{Expr, ExprKind, LangItem, MatchSource};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`.
|
||||
@ -90,7 +91,7 @@ fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opti
|
||||
fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
let ty = ty.peel_refs();
|
||||
is_type_diagnostic_item(cx, ty, sym!(vec_type))
|
||||
is_type_diagnostic_item(cx, ty, sym::vec_type)
|
||||
}
|
||||
|
||||
fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
|
@ -22,7 +22,7 @@ use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty, TyS};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::Symbol;
|
||||
use rustc_span::{sym, Symbol};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::Bound;
|
||||
@ -662,7 +662,7 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp
|
||||
}
|
||||
} else {
|
||||
// not a block, don't lint
|
||||
return;
|
||||
return;
|
||||
};
|
||||
|
||||
let ty = cx.typeck_results().expr_ty(ex);
|
||||
@ -840,7 +840,7 @@ fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms
|
||||
|
||||
fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
|
||||
let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
|
||||
if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) {
|
||||
if is_type_diagnostic_item(cx, ex_ty, sym::result_type) {
|
||||
for arm in arms {
|
||||
if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind {
|
||||
let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
|
||||
@ -1509,6 +1509,7 @@ mod redundant_pattern_match {
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
|
||||
@ -1552,7 +1553,7 @@ mod redundant_pattern_match {
|
||||
if_chain! {
|
||||
if keyword == "while";
|
||||
if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
|
||||
if method_path.ident.name == sym!(next);
|
||||
if method_path.ident.name == sym::next;
|
||||
if match_trait_method(cx, op, &paths::ITERATOR);
|
||||
then {
|
||||
return;
|
||||
|
@ -7,6 +7,7 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::sym;
|
||||
|
||||
/// Checks for the `INEFFICIENT_TO_STRING` lint
|
||||
pub fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) {
|
||||
@ -50,7 +51,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
|
||||
if is_type_diagnostic_item(cx, ty, sym::string_type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -32,8 +32,7 @@ use crate::utils::{
|
||||
is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath,
|
||||
match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty,
|
||||
single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
|
||||
span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth,
|
||||
SpanlessEq,
|
||||
span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq,
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -1291,8 +1290,8 @@ declare_clippy_lint! {
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns when using `push_str` with a single-character string literal,
|
||||
/// and `push` with a `char` would work fine.
|
||||
/// **What it does:** Warns when using `push_str`/`insert_str` with a single-character string literal
|
||||
/// where `push`/`insert` with a `char` would work fine.
|
||||
///
|
||||
/// **Why is this bad?** It's less clear that we are pushing a single character.
|
||||
///
|
||||
@ -1301,16 +1300,18 @@ declare_clippy_lint! {
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut string = String::new();
|
||||
/// string.insert_str(0, "R");
|
||||
/// string.push_str("R");
|
||||
/// ```
|
||||
/// Could be written as
|
||||
/// ```rust
|
||||
/// let mut string = String::new();
|
||||
/// string.insert(0, 'R');
|
||||
/// string.push('R');
|
||||
/// ```
|
||||
pub SINGLE_CHAR_PUSH_STR,
|
||||
pub SINGLE_CHAR_ADD_STR,
|
||||
style,
|
||||
"`push_str()` used with a single-character string literal as parameter"
|
||||
"`push_str()` or `insert_str()` used with a single-character string literal as parameter"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -1349,6 +1350,60 @@ declare_clippy_lint! {
|
||||
"using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.map(_).collect::<Result<(),_>()`.
|
||||
///
|
||||
/// **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// (0..3).try_for_each(|t| Err(t));
|
||||
/// ```
|
||||
pub MAP_COLLECT_RESULT_UNIT,
|
||||
style,
|
||||
"using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator`
|
||||
/// trait.
|
||||
///
|
||||
/// **Why is this bad?** It is recommended style to use collect. See
|
||||
/// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::iter::FromIterator;
|
||||
///
|
||||
/// let five_fives = std::iter::repeat(5).take(5);
|
||||
///
|
||||
/// let v = Vec::from_iter(five_fives);
|
||||
///
|
||||
/// assert_eq!(v, vec![5, 5, 5, 5, 5]);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let five_fives = std::iter::repeat(5).take(5);
|
||||
///
|
||||
/// let v: Vec<i32> = five_fives.collect();
|
||||
///
|
||||
/// assert_eq!(v, vec![5, 5, 5, 5, 5]);
|
||||
/// ```
|
||||
pub FROM_ITER_INSTEAD_OF_COLLECT,
|
||||
style,
|
||||
"use `.collect()` instead of `::from_iter()`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Methods => [
|
||||
UNWRAP_USED,
|
||||
EXPECT_USED,
|
||||
@ -1370,7 +1425,7 @@ declare_lint_pass!(Methods => [
|
||||
INEFFICIENT_TO_STRING,
|
||||
NEW_RET_NO_SELF,
|
||||
SINGLE_CHAR_PATTERN,
|
||||
SINGLE_CHAR_PUSH_STR,
|
||||
SINGLE_CHAR_ADD_STR,
|
||||
SEARCH_IS_SOME,
|
||||
FILTER_NEXT,
|
||||
SKIP_WHILE_NEXT,
|
||||
@ -1398,6 +1453,8 @@ declare_lint_pass!(Methods => [
|
||||
FILETYPE_IS_FILE,
|
||||
OPTION_AS_REF_DEREF,
|
||||
UNNECESSARY_LAZY_EVALUATIONS,
|
||||
MAP_COLLECT_RESULT_UNIT,
|
||||
FROM_ITER_INSTEAD_OF_COLLECT,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
@ -1479,10 +1536,18 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"),
|
||||
["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"),
|
||||
["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"),
|
||||
["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
_ => {},
|
||||
}
|
||||
|
||||
match expr.kind {
|
||||
hir::ExprKind::Call(ref func, ref args) => {
|
||||
if let hir::ExprKind::Path(path) = &func.kind {
|
||||
if match_qpath(path, &["from_iter"]) {
|
||||
lint_from_iter(cx, expr, args);
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => {
|
||||
lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
|
||||
lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
|
||||
@ -1499,6 +1564,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
||||
if match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
|
||||
lint_single_char_push_string(cx, expr, args);
|
||||
} else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) {
|
||||
lint_single_char_insert_string(cx, expr, args);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1655,7 +1722,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.tcx.sess, item.span);
|
||||
if item.ident.name == sym!(new);
|
||||
if item.ident.name == sym::new;
|
||||
if let TraitItemKind::Fn(_, _) = item.kind;
|
||||
let ret_ty = return_ty(cx, item.hir_id);
|
||||
let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty();
|
||||
@ -1712,7 +1779,7 @@ fn lint_or_fun_call<'tcx>(
|
||||
"try this",
|
||||
format!(
|
||||
"{}.unwrap_or_default()",
|
||||
snippet_with_applicability(cx, self_expr.span, "_", &mut applicability)
|
||||
snippet_with_applicability(cx, self_expr.span, "..", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
@ -1745,7 +1812,7 @@ fn lint_or_fun_call<'tcx>(
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if is_type_diagnostic_item(cx, ty, sym!(vec_type)) {
|
||||
if is_type_diagnostic_item(cx, ty, sym::vec_type) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1842,11 +1909,11 @@ fn lint_expect_fun_call(
|
||||
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr,
|
||||
hir::ExprKind::MethodCall(method_name, _, call_args, _) => {
|
||||
if call_args.len() == 1
|
||||
&& (method_name.ident.name == sym!(as_str) || method_name.ident.name == sym!(as_ref))
|
||||
&& (method_name.ident.name == sym::as_str || method_name.ident.name == sym!(as_ref))
|
||||
&& {
|
||||
let arg_type = cx.typeck_results().expr_ty(&call_args[0]);
|
||||
let base_type = arg_type.peel_refs();
|
||||
*base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type))
|
||||
*base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::string_type)
|
||||
}
|
||||
{
|
||||
&call_args[0]
|
||||
@ -1864,7 +1931,7 @@ fn lint_expect_fun_call(
|
||||
// converted to string.
|
||||
fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
|
||||
let arg_ty = cx.typeck_results().expr_ty(arg);
|
||||
if is_type_diagnostic_item(cx, arg_ty, sym!(string_type)) {
|
||||
if is_type_diagnostic_item(cx, arg_ty, sym::string_type) {
|
||||
return false;
|
||||
}
|
||||
if let ty::Ref(_, ty, ..) = arg_ty.kind() {
|
||||
@ -1951,9 +2018,9 @@ fn lint_expect_fun_call(
|
||||
}
|
||||
|
||||
let receiver_type = cx.typeck_results().expr_ty_adjusted(&args[0]);
|
||||
let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym!(option_type)) {
|
||||
let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::option_type) {
|
||||
"||"
|
||||
} else if is_type_diagnostic_item(cx, receiver_type, sym!(result_type)) {
|
||||
} else if is_type_diagnostic_item(cx, receiver_type, sym::result_type) {
|
||||
"|_|"
|
||||
} else {
|
||||
return;
|
||||
@ -2119,7 +2186,7 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::
|
||||
return;
|
||||
};
|
||||
|
||||
let snippet = snippet_with_macro_callsite(cx, arg.span, "_");
|
||||
let snippet = snippet_with_macro_callsite(cx, arg.span, "..");
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
@ -2140,7 +2207,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E
|
||||
let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
|
||||
let ref_str = if *self_ty.kind() == ty::Str {
|
||||
""
|
||||
} else if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) {
|
||||
} else if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
|
||||
"&"
|
||||
} else {
|
||||
return;
|
||||
@ -2155,9 +2222,9 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E
|
||||
"try this",
|
||||
format!(
|
||||
"{}.push_str({}{})",
|
||||
snippet_with_applicability(cx, args[0].span, "_", &mut applicability),
|
||||
snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
|
||||
ref_str,
|
||||
snippet_with_applicability(cx, target.span, "_", &mut applicability)
|
||||
snippet_with_applicability(cx, target.span, "..", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
@ -2166,14 +2233,14 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E
|
||||
|
||||
fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
|
||||
if is_type_diagnostic_item(cx, obj_ty, sym!(string_type)) {
|
||||
if is_type_diagnostic_item(cx, obj_ty, sym::string_type) {
|
||||
lint_string_extend(cx, expr, args);
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_iter_cloned_collect<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) {
|
||||
if_chain! {
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym!(vec_type));
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type);
|
||||
if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0]));
|
||||
if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite());
|
||||
|
||||
@ -2326,7 +2393,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym!(vec_type))
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym::vec_type)
|
||||
|| matches!(
|
||||
&cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(),
|
||||
ty::Array(_, _)
|
||||
@ -2359,7 +2426,7 @@ fn lint_iter_nth<'tcx>(
|
||||
let mut_str = if is_mut { "_mut" } else { "" };
|
||||
let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])).is_some() {
|
||||
"slice"
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vec_type)) {
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vec_type) {
|
||||
"Vec"
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vecdeque_type)) {
|
||||
"VecDeque"
|
||||
@ -2404,7 +2471,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args:
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let expr_ty = cx.typeck_results().expr_ty(&get_args[0]);
|
||||
let get_args_str = if get_args.len() > 1 {
|
||||
snippet_with_applicability(cx, get_args[1].span, "_", &mut applicability)
|
||||
snippet_with_applicability(cx, get_args[1].span, "..", &mut applicability)
|
||||
} else {
|
||||
return; // not linting on a .get().unwrap() chain or variant
|
||||
};
|
||||
@ -2412,7 +2479,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args:
|
||||
let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() {
|
||||
needs_ref = get_args_str.parse::<usize>().is_ok();
|
||||
"slice"
|
||||
} else if is_type_diagnostic_item(cx, expr_ty, sym!(vec_type)) {
|
||||
} else if is_type_diagnostic_item(cx, expr_ty, sym::vec_type) {
|
||||
needs_ref = get_args_str.parse::<usize>().is_ok();
|
||||
"Vec"
|
||||
} else if is_type_diagnostic_item(cx, expr_ty, sym!(vecdeque_type)) {
|
||||
@ -2464,7 +2531,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args:
|
||||
format!(
|
||||
"{}{}[{}]",
|
||||
borrow_str,
|
||||
snippet_with_applicability(cx, get_args[0].span, "_", &mut applicability),
|
||||
snippet_with_applicability(cx, get_args[0].span, "..", &mut applicability),
|
||||
get_args_str
|
||||
),
|
||||
applicability,
|
||||
@ -2480,7 +2547,7 @@ fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[
|
||||
cx,
|
||||
ITER_SKIP_NEXT,
|
||||
expr.span.trim_start(caller.span).unwrap(),
|
||||
"called `skip(x).next()` on an iterator",
|
||||
"called `skip(..).next()` on an iterator",
|
||||
"use `nth` instead",
|
||||
hint,
|
||||
Applicability::MachineApplicable,
|
||||
@ -2498,7 +2565,7 @@ fn derefs_to_slice<'tcx>(
|
||||
match ty.kind() {
|
||||
ty::Slice(_) => true,
|
||||
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
|
||||
ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)),
|
||||
ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type),
|
||||
ty::Array(_, size) => size
|
||||
.try_eval_usize(cx.tcx, cx.param_env)
|
||||
.map_or(false, |size| size < 32),
|
||||
@ -2508,7 +2575,7 @@ fn derefs_to_slice<'tcx>(
|
||||
}
|
||||
|
||||
if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind {
|
||||
if path.ident.name == sym!(iter) && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) {
|
||||
if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) {
|
||||
Some(&args[0])
|
||||
} else {
|
||||
None
|
||||
@ -2533,9 +2600,9 @@ fn derefs_to_slice<'tcx>(
|
||||
fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(&unwrap_args[0]).peel_refs();
|
||||
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) {
|
||||
Some((UNWRAP_USED, "an Option", "None"))
|
||||
} else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) {
|
||||
} else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) {
|
||||
Some((UNWRAP_USED, "a Result", "Err"))
|
||||
} else {
|
||||
None
|
||||
@ -2585,7 +2652,7 @@ fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::E
|
||||
fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Expr<'_>]) {
|
||||
if_chain! {
|
||||
// lint if the caller of `ok()` is a `Result`
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&ok_args[0]), sym!(result_type));
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&ok_args[0]), sym::result_type);
|
||||
let result_type = cx.typeck_results().expr_ty(&ok_args[0]);
|
||||
if let Some(error_type) = get_error_type(cx, result_type);
|
||||
if has_debug_impl(error_type, cx);
|
||||
@ -2615,7 +2682,7 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
|
||||
_ => map_closure_ty.fn_sig(cx.tcx),
|
||||
};
|
||||
let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&map_closure_sig.output());
|
||||
is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type))
|
||||
is_type_diagnostic_item(cx, map_closure_return_ty, sym::option_type)
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
@ -2641,7 +2708,7 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
|
||||
}
|
||||
|
||||
// lint if caller of `.map().flatten()` is an Option
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) {
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) {
|
||||
let func_snippet = snippet(cx, map_args[1].span, "..");
|
||||
let hint = format!(".and_then({})", func_snippet);
|
||||
span_lint_and_sugg(
|
||||
@ -2665,8 +2732,8 @@ fn lint_map_unwrap_or_else<'tcx>(
|
||||
unwrap_args: &'tcx [hir::Expr<'_>],
|
||||
) -> bool {
|
||||
// lint if the caller of `map()` is an `Option`
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type));
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(result_type));
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type);
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type);
|
||||
|
||||
if is_option || is_result {
|
||||
// Don't make a suggestion that may fail to compile due to mutably borrowing
|
||||
@ -2683,11 +2750,11 @@ fn lint_map_unwrap_or_else<'tcx>(
|
||||
|
||||
// lint message
|
||||
let msg = if is_option {
|
||||
"called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling \
|
||||
`map_or_else(g, f)` instead"
|
||||
"called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling \
|
||||
`map_or_else(<g>, <f>)` instead"
|
||||
} else {
|
||||
"called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling \
|
||||
`.map_or_else(g, f)` instead"
|
||||
"called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling \
|
||||
`.map_or_else(<g>, <f>)` instead"
|
||||
};
|
||||
// get snippets for args to map() and unwrap_or_else()
|
||||
let map_snippet = snippet(cx, map_args[1].span, "..");
|
||||
@ -2697,16 +2764,15 @@ fn lint_map_unwrap_or_else<'tcx>(
|
||||
let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
|
||||
let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt();
|
||||
if same_span && !multiline {
|
||||
span_lint_and_note(
|
||||
let var_snippet = snippet(cx, map_args[0].span, "..");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_UNWRAP_OR,
|
||||
expr.span,
|
||||
msg,
|
||||
None,
|
||||
&format!(
|
||||
"replace `map({0}).unwrap_or_else({1})` with `map_or_else({1}, {0})`",
|
||||
map_snippet, unwrap_snippet,
|
||||
),
|
||||
"try this",
|
||||
format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return true;
|
||||
} else if same_span && multiline {
|
||||
@ -2720,8 +2786,8 @@ fn lint_map_unwrap_or_else<'tcx>(
|
||||
|
||||
/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
|
||||
fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_or_args: &'tcx [hir::Expr<'_>]) {
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym!(option_type));
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym!(result_type));
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::option_type);
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::result_type);
|
||||
|
||||
// There are two variants of this `map_or` lint:
|
||||
// (1) using `map_or` as an adapter from `Result<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 {
|
||||
let self_snippet = snippet(cx, map_or_args[0].span, "..");
|
||||
let func_snippet = snippet(cx, map_or_args[2].span, "..");
|
||||
let msg = "called `map_or(None, f)` on an `Option` value. This can be done more directly by calling \
|
||||
`and_then(f)` instead";
|
||||
let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
|
||||
`and_then(..)` instead";
|
||||
(
|
||||
OPTION_MAP_OR_NONE,
|
||||
msg,
|
||||
@ -2792,18 +2858,20 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
|
||||
fn lint_filter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) {
|
||||
// lint if caller of `.filter().next()` is an Iterator
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling \
|
||||
`.find(p)` instead.";
|
||||
let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
|
||||
`.find(..)` instead.";
|
||||
let filter_snippet = snippet(cx, filter_args[1].span, "..");
|
||||
if filter_snippet.lines().count() <= 1 {
|
||||
let iter_snippet = snippet(cx, filter_args[0].span, "..");
|
||||
// add note if not multi-line
|
||||
span_lint_and_note(
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
FILTER_NEXT,
|
||||
expr.span,
|
||||
msg,
|
||||
None,
|
||||
&format!("replace `filter({0}).next()` with `find({0})`", filter_snippet),
|
||||
"try this",
|
||||
format!("{}.find({})", iter_snippet, filter_snippet),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
span_lint(cx, FILTER_NEXT, expr.span, msg);
|
||||
@ -2823,9 +2891,9 @@ fn lint_skip_while_next<'tcx>(
|
||||
cx,
|
||||
SKIP_WHILE_NEXT,
|
||||
expr.span,
|
||||
"called `skip_while(p).next()` on an `Iterator`",
|
||||
"called `skip_while(<p>).next()` on an `Iterator`",
|
||||
None,
|
||||
"this is more succinctly expressed by calling `.find(!p)` instead",
|
||||
"this is more succinctly expressed by calling `.find(!<p>)` instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -2839,7 +2907,7 @@ fn lint_filter_map<'tcx>(
|
||||
) {
|
||||
// lint if caller of `.filter().map()` is an Iterator
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `filter(p).map(q)` on an `Iterator`";
|
||||
let msg = "called `filter(..).map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead";
|
||||
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
|
||||
}
|
||||
@ -2848,17 +2916,19 @@ fn lint_filter_map<'tcx>(
|
||||
/// lint use of `filter_map().next()` for `Iterators`
|
||||
fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) {
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling \
|
||||
`.find_map(p)` instead.";
|
||||
let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
|
||||
`.find_map(..)` instead.";
|
||||
let filter_snippet = snippet(cx, filter_args[1].span, "..");
|
||||
if filter_snippet.lines().count() <= 1 {
|
||||
span_lint_and_note(
|
||||
let iter_snippet = snippet(cx, filter_args[0].span, "..");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
FILTER_MAP_NEXT,
|
||||
expr.span,
|
||||
msg,
|
||||
None,
|
||||
&format!("replace `filter_map({0}).next()` with `find_map({0})`", filter_snippet),
|
||||
"try this",
|
||||
format!("{}.find_map({})", iter_snippet, filter_snippet),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
span_lint(cx, FILTER_MAP_NEXT, expr.span, msg);
|
||||
@ -2875,7 +2945,7 @@ fn lint_find_map<'tcx>(
|
||||
) {
|
||||
// lint if caller of `.filter().map()` is an Iterator
|
||||
if match_trait_method(cx, &map_args[0], &paths::ITERATOR) {
|
||||
let msg = "called `find(p).map(q)` on an `Iterator`";
|
||||
let msg = "called `find(..).map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by calling `.find_map(..)` instead";
|
||||
span_lint_and_help(cx, FIND_MAP, expr.span, msg, None, hint);
|
||||
}
|
||||
@ -2890,7 +2960,7 @@ fn lint_filter_map_map<'tcx>(
|
||||
) {
|
||||
// lint if caller of `.filter().map()` is an Iterator
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `filter_map(p).map(q)` on an `Iterator`";
|
||||
let msg = "called `filter_map(..).map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead";
|
||||
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
|
||||
}
|
||||
@ -2905,7 +2975,7 @@ fn lint_filter_flat_map<'tcx>(
|
||||
) {
|
||||
// lint if caller of `.filter().flat_map()` is an Iterator
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `filter(p).flat_map(q)` on an `Iterator`";
|
||||
let msg = "called `filter(..).flat_map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
|
||||
and filtering by returning `iter::empty()`";
|
||||
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
|
||||
@ -2921,7 +2991,7 @@ fn lint_filter_map_flat_map<'tcx>(
|
||||
) {
|
||||
// lint if caller of `.filter_map().flat_map()` is an Iterator
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `filter_map(p).flat_map(q)` on an `Iterator`";
|
||||
let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
|
||||
and filtering by returning `iter::empty()`";
|
||||
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
|
||||
@ -3075,7 +3145,7 @@ fn lint_chars_cmp(
|
||||
if arg_char.len() == 1;
|
||||
if let hir::ExprKind::Path(ref qpath) = fun.kind;
|
||||
if let Some(segment) = single_segment_path(qpath);
|
||||
if segment.ident.name == sym!(Some);
|
||||
if segment.ident.name == sym::Some;
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
|
||||
@ -3092,9 +3162,9 @@ fn lint_chars_cmp(
|
||||
"like this",
|
||||
format!("{}{}.{}({})",
|
||||
if info.eq { "" } else { "!" },
|
||||
snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability),
|
||||
snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
|
||||
suggest,
|
||||
snippet_with_applicability(cx, arg_char[0].span, "_", &mut applicability)),
|
||||
snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
|
||||
applicability,
|
||||
);
|
||||
|
||||
@ -3141,7 +3211,7 @@ fn lint_chars_cmp_with_unwrap<'tcx>(
|
||||
"like this",
|
||||
format!("{}{}.{}('{}')",
|
||||
if info.eq { "" } else { "!" },
|
||||
snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability),
|
||||
snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
|
||||
suggest,
|
||||
c),
|
||||
applicability,
|
||||
@ -3177,7 +3247,7 @@ fn get_hint_if_single_char_arg(
|
||||
if let hir::ExprKind::Lit(lit) = &arg.kind;
|
||||
if let ast::LitKind::Str(r, style) = lit.node;
|
||||
let string = r.as_str();
|
||||
if string.len() == 1;
|
||||
if string.chars().count() == 1;
|
||||
then {
|
||||
let snip = snippet_with_applicability(cx, arg.span, &string, applicability);
|
||||
let ch = if let ast::StrStyle::Raw(nhash) = style {
|
||||
@ -3216,11 +3286,12 @@ fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &h
|
||||
fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) {
|
||||
let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
|
||||
let base_string_snippet =
|
||||
snippet_with_applicability(cx, args[0].span.source_callsite(), "..", &mut applicability);
|
||||
let sugg = format!("{}.push({})", base_string_snippet, extension_string);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SINGLE_CHAR_PUSH_STR,
|
||||
SINGLE_CHAR_ADD_STR,
|
||||
expr.span,
|
||||
"calling `push_str()` using a single-character string literal",
|
||||
"consider using `push` with a character literal",
|
||||
@ -3230,6 +3301,26 @@ fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args
|
||||
}
|
||||
}
|
||||
|
||||
/// lint for length-1 `str`s as argument for `insert_str`
|
||||
fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) {
|
||||
let base_string_snippet =
|
||||
snippet_with_applicability(cx, args[0].span.source_callsite(), "_", &mut applicability);
|
||||
let pos_arg = snippet_with_applicability(cx, args[1].span, "..", &mut applicability);
|
||||
let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SINGLE_CHAR_ADD_STR,
|
||||
expr.span,
|
||||
"calling `insert_str()` using a single-character string literal",
|
||||
"consider using `insert` with a character literal",
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks for the `USELESS_ASREF` lint.
|
||||
fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) {
|
||||
// when we get here, we've already checked that the call name is "as_ref" or "as_mut"
|
||||
@ -3259,7 +3350,7 @@ fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_re
|
||||
expr.span,
|
||||
&format!("this call to `{}` does nothing", call_name),
|
||||
"try this",
|
||||
snippet_with_applicability(cx, recvr.span, "_", &mut applicability).to_string(),
|
||||
snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
@ -3350,7 +3441,7 @@ fn lint_option_as_ref_deref<'tcx>(
|
||||
let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not);
|
||||
|
||||
let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]);
|
||||
if !is_type_diagnostic_item(cx, option_ty, sym!(option_type)) {
|
||||
if !is_type_diagnostic_item(cx, option_ty, sym::option_type) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3445,10 +3536,46 @@ fn lint_option_as_ref_deref<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_map_collect(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
map_args: &[hir::Expr<'_>],
|
||||
collect_args: &[hir::Expr<'_>],
|
||||
) {
|
||||
if_chain! {
|
||||
// called on Iterator
|
||||
if let [map_expr] = collect_args;
|
||||
if match_trait_method(cx, map_expr, &paths::ITERATOR);
|
||||
// return of collect `Result<(),_>`
|
||||
let collect_ret_ty = cx.typeck_results().expr_ty(expr);
|
||||
if is_type_diagnostic_item(cx, collect_ret_ty, sym::result_type);
|
||||
if let ty::Adt(_, substs) = collect_ret_ty.kind();
|
||||
if let Some(result_t) = substs.types().next();
|
||||
if result_t.is_unit();
|
||||
// get parts for snippet
|
||||
if let [iter, map_fn] = map_args;
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_COLLECT_RESULT_UNIT,
|
||||
expr.span,
|
||||
"`.map().collect()` can be replaced with `.try_for_each()`",
|
||||
"try this",
|
||||
format!(
|
||||
"{}.try_for_each({})",
|
||||
snippet(cx, iter.span, ".."),
|
||||
snippet(cx, map_fn.span, "..")
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a `Result<T, E>` type, return its error type (`E`).
|
||||
fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
|
||||
match ty.kind() {
|
||||
ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym!(result_type)) => substs.types().nth(1),
|
||||
ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::result_type) => substs.types().nth(1),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -3770,6 +3897,28 @@ fn lint_filetype_is_file(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir
|
||||
span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg);
|
||||
}
|
||||
|
||||
fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
let arg_ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
|
||||
let from_iter_id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap();
|
||||
let iter_id = get_trait_def_id(cx, &paths::ITERATOR).unwrap();
|
||||
|
||||
if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]) {
|
||||
// `expr` implements `FromIterator` trait
|
||||
let iter_expr = snippet(cx, args[0].span, "..");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
FROM_ITER_INSTEAD_OF_COLLECT,
|
||||
expr.span,
|
||||
"usage of `FromIterator::from_iter`",
|
||||
"use `.collect()` instead of `::from_iter()`",
|
||||
format!("{}.collect()", iter_expr),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
|
||||
expected.constness == actual.constness
|
||||
&& expected.unsafety == actual.unsafety
|
||||
|
@ -7,7 +7,7 @@ use rustc_hir::{self, HirId, Path};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{sym, Symbol};
|
||||
|
||||
use super::MAP_UNWRAP_OR;
|
||||
|
||||
@ -20,7 +20,7 @@ pub(super) fn lint<'tcx>(
|
||||
map_span: Span,
|
||||
) {
|
||||
// lint if the caller of `map()` is an `Option`
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) {
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) {
|
||||
if !is_copy(cx, cx.typeck_results().expr_ty(&unwrap_args[1])) {
|
||||
// Do not lint if the `map` argument uses identifiers in the `map`
|
||||
// argument that are also used in the `unwrap_or` argument
|
||||
@ -53,15 +53,15 @@ pub(super) fn lint<'tcx>(
|
||||
// lint message
|
||||
// comparing the snippet from source to raw text ("None") below is safe
|
||||
// because we already have checked the type.
|
||||
let arg = if unwrap_snippet == "None" { "None" } else { "a" };
|
||||
let arg = if unwrap_snippet == "None" { "None" } else { "<a>" };
|
||||
let unwrap_snippet_none = unwrap_snippet == "None";
|
||||
let suggest = if unwrap_snippet_none {
|
||||
"and_then(f)"
|
||||
"and_then(<f>)"
|
||||
} else {
|
||||
"map_or(a, f)"
|
||||
"map_or(<a>, <f>)"
|
||||
};
|
||||
let msg = &format!(
|
||||
"called `map(f).unwrap_or({})` on an `Option` value. \
|
||||
"called `map(<f>).unwrap_or({})` on an `Option` value. \
|
||||
This can be done more directly by calling `{}` instead",
|
||||
arg, suggest
|
||||
);
|
||||
|
@ -3,6 +3,7 @@ use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::UNNECESSARY_LAZY_EVALUATIONS;
|
||||
|
||||
@ -14,8 +15,8 @@ pub(super) fn lint<'tcx>(
|
||||
args: &'tcx [hir::Expr<'_>],
|
||||
simplify_using: &str,
|
||||
) {
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type));
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type));
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::option_type);
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::result_type);
|
||||
|
||||
if is_option || is_result {
|
||||
if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {
|
||||
|
@ -7,6 +7,7 @@ use rustc_hir::{
|
||||
StmtKind, TyKind, UnOp,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::hygiene::DesugaringKind;
|
||||
@ -271,13 +272,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
|
||||
k: FnKind<'tcx>,
|
||||
decl: &'tcx FnDecl<'_>,
|
||||
body: &'tcx Body<'_>,
|
||||
_: Span,
|
||||
span: Span,
|
||||
_: HirId,
|
||||
) {
|
||||
if let FnKind::Closure(_) = k {
|
||||
// Does not apply to closures
|
||||
return;
|
||||
}
|
||||
if in_external_macro(cx.tcx.sess, span) {
|
||||
return;
|
||||
}
|
||||
for arg in iter_input_pats(decl, body) {
|
||||
if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind {
|
||||
span_lint(
|
||||
@ -293,13 +297,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
|
||||
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.tcx.sess, stmt.span);
|
||||
if let StmtKind::Local(ref local) = stmt.kind;
|
||||
if let PatKind::Binding(an, .., name, None) = local.pat.kind;
|
||||
if let Some(ref init) = local.init;
|
||||
if !higher::is_from_for_desugar(local);
|
||||
then {
|
||||
if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut {
|
||||
let sugg_init = if init.span.from_expansion() {
|
||||
// use the macro callsite when the init span (but not the whole local span)
|
||||
// comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];`
|
||||
let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() {
|
||||
Sugg::hir_with_macro_callsite(cx, init, "..")
|
||||
} else {
|
||||
Sugg::hir(cx, init, "..")
|
||||
@ -310,7 +317,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
|
||||
("", sugg_init.addr())
|
||||
};
|
||||
let tyopt = if let Some(ref ty) = local.ty {
|
||||
format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "_"))
|
||||
format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, ".."))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
@ -326,7 +333,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
|
||||
"try",
|
||||
format!(
|
||||
"let {name}{tyopt} = {initref};",
|
||||
name=snippet(cx, name.span, "_"),
|
||||
name=snippet(cx, name.span, ".."),
|
||||
tyopt=tyopt,
|
||||
initref=initref,
|
||||
),
|
||||
|
@ -14,6 +14,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns if there is missing doc for any documentable item
|
||||
@ -105,10 +106,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, attrs: &'tcx [ast::Attribute]) {
|
||||
let doc_hidden = self.doc_hidden()
|
||||
|| attrs.iter().any(|attr| {
|
||||
attr.has_name(sym!(doc))
|
||||
attr.has_name(sym::doc)
|
||||
&& match attr.meta_item_list() {
|
||||
None => false,
|
||||
Some(l) => attr::list_contains_name(&l[..], sym!(hidden)),
|
||||
Some(l) => attr::list_contains_name(&l[..], sym::hidden),
|
||||
}
|
||||
});
|
||||
self.doc_hidden_stack.push(doc_hidden);
|
||||
@ -128,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
hir::ItemKind::Enum(..) => "an enum",
|
||||
hir::ItemKind::Fn(..) => {
|
||||
// ignore main()
|
||||
if it.ident.name == sym!(main) {
|
||||
if it.ident.name == sym::main {
|
||||
let def_id = it.hir_id.owner;
|
||||
let def_key = cx.tcx.hir().def_key(def_id);
|
||||
if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) {
|
||||
|
@ -4,6 +4,7 @@ use rustc_hir as hir;
|
||||
use rustc_lint::{self, LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** it lints if an exported function, method, trait method with default impl,
|
||||
@ -57,7 +58,7 @@ declare_clippy_lint! {
|
||||
}
|
||||
|
||||
fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
|
||||
let has_inline = attrs.iter().any(|a| a.has_name(sym!(inline)));
|
||||
let has_inline = attrs.iter().any(|a| a.has_name(sym::inline));
|
||||
if !has_inline {
|
||||
span_lint(
|
||||
cx,
|
||||
|
@ -10,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for address of operations (`&`) that are going to
|
||||
@ -112,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
|
||||
}
|
||||
|
||||
fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if item.attrs.iter().any(|a| a.has_name(sym!(automatically_derived))) {
|
||||
if item.attrs.iter().any(|a| a.has_name(sym::automatically_derived)) {
|
||||
debug_assert!(self.derived_item.is_none());
|
||||
self.derived_item = Some(item.hir_id);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, TypeFoldable};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_trait_selection::traits;
|
||||
use rustc_trait_selection::traits::misc::can_type_implement_copy;
|
||||
@ -204,12 +204,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
||||
|
||||
let deref_span = spans_need_deref.get(&canonical_id);
|
||||
if_chain! {
|
||||
if is_type_diagnostic_item(cx, ty, sym!(vec_type));
|
||||
if is_type_diagnostic_item(cx, ty, sym::vec_type);
|
||||
if let Some(clone_spans) =
|
||||
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]);
|
||||
if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind;
|
||||
if let Some(elem_ty) = path.segments.iter()
|
||||
.find(|seg| seg.ident.name == sym!(Vec))
|
||||
.find(|seg| seg.ident.name == sym::Vec)
|
||||
.and_then(|ps| ps.args.as_ref())
|
||||
.map(|params| params.args.iter().find_map(|arg| match arg {
|
||||
GenericArg::Type(ty) => Some(ty),
|
||||
@ -243,7 +243,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
||||
}
|
||||
}
|
||||
|
||||
if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
|
||||
if is_type_diagnostic_item(cx, ty, sym::string_type) {
|
||||
if let Some(clone_spans) =
|
||||
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
|
||||
diag.span_suggestion(
|
||||
@ -302,7 +302,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
||||
/// Functions marked with these attributes must have the exact signature.
|
||||
fn requires_exact_signature(attrs: &[Attribute]) -> bool {
|
||||
attrs.iter().any(|attr| {
|
||||
[sym!(proc_macro), sym!(proc_macro_attribute), sym!(proc_macro_derive)]
|
||||
[sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
|
||||
.iter()
|
||||
.any(|&allow| attr.has_name(allow))
|
||||
})
|
||||
|
@ -9,6 +9,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{Ty, TyS};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for types with a `fn new() -> Self` method and no
|
||||
@ -91,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
|
||||
// impl of `Default`
|
||||
return;
|
||||
}
|
||||
if sig.decl.inputs.is_empty() && name == sym!(new) && cx.access_levels.is_reachable(id) {
|
||||
if sig.decl.inputs.is_empty() && name == sym::new && cx.access_levels.is_reachable(id) {
|
||||
let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id));
|
||||
let self_ty = cx.tcx.type_of(self_def_id);
|
||||
if_chain! {
|
||||
|
@ -7,6 +7,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
@ -384,7 +385,7 @@ impl EarlyLintPass for NonExpressiveNames {
|
||||
}
|
||||
|
||||
fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
|
||||
if !attrs.iter().any(|attr| attr.has_name(sym!(test))) {
|
||||
if !attrs.iter().any(|attr| attr.has_name(sym::test)) {
|
||||
let mut visitor = SimilarNamesLocalVisitor {
|
||||
names: Vec::new(),
|
||||
cx,
|
||||
|
@ -8,6 +8,7 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
@ -66,7 +67,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
|
||||
fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
|
||||
if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind {
|
||||
path.ident.name.to_ident_string() == "ok"
|
||||
&& is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym!(result_type))
|
||||
&& is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym::result_type)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use rustc_hir::Expr;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result.
|
||||
@ -40,7 +40,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
|
||||
hir_id: hir::HirId,
|
||||
) {
|
||||
if !matches!(fn_kind, FnKind::Closure(_))
|
||||
&& is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type))
|
||||
&& is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type)
|
||||
{
|
||||
lint_impl_body(cx, span, body);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use if_chain::if_chain;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for manual re-implementations of `PartialEq::ne`.
|
||||
@ -39,7 +40,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl {
|
||||
if trait_ref.path.res.def_id() == eq_trait;
|
||||
then {
|
||||
for impl_item in impl_items {
|
||||
if impl_item.ident.name == sym!(ne) {
|
||||
if impl_item.ident.name == sym::ne {
|
||||
span_lint_hir(
|
||||
cx,
|
||||
PARTIALEQ_NE_IMPL,
|
||||
|
@ -10,7 +10,7 @@ use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutabil
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_target::abi::LayoutOf;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_target::spec::Target;
|
||||
@ -230,8 +230,8 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue {
|
||||
}
|
||||
for a in attrs {
|
||||
if let Some(meta_items) = a.meta_item_list() {
|
||||
if a.has_name(sym!(proc_macro_derive))
|
||||
|| (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always)))
|
||||
if a.has_name(sym::proc_macro_derive)
|
||||
|| (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::MultiSpan;
|
||||
use rustc_span::{sym, MultiSpan};
|
||||
use std::borrow::Cow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -181,7 +181,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
|
||||
}
|
||||
|
||||
if let ty::Ref(_, ty, Mutability::Not) = ty.kind() {
|
||||
if is_type_diagnostic_item(cx, ty, sym!(vec_type)) {
|
||||
if is_type_diagnostic_item(cx, ty, sym::vec_type) {
|
||||
let mut ty_snippet = None;
|
||||
if_chain! {
|
||||
if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind;
|
||||
@ -225,7 +225,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::string_type) {
|
||||
if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
@ -3,6 +3,7 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
use std::fmt;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -92,7 +93,7 @@ fn expr_as_ptr_offset_call<'tcx>(
|
||||
) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> {
|
||||
if let ExprKind::MethodCall(ref path_segment, _, ref args, _) = expr.kind {
|
||||
if is_expr_ty_raw_ptr(cx, &args[0]) {
|
||||
if path_segment.ident.name == sym!(offset) {
|
||||
if path_segment.ident.name == sym::offset {
|
||||
return Some((&args[0], &args[1], Method::Offset));
|
||||
}
|
||||
if path_segment.ident.name == sym!(wrapping_offset) {
|
||||
|
@ -4,6 +4,7 @@ use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::{
|
||||
@ -143,7 +144,7 @@ impl QuestionMark {
|
||||
fn is_option(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
|
||||
let expr_ty = cx.typeck_results().expr_ty(expression);
|
||||
|
||||
is_type_diagnostic_item(cx, expr_ty, sym!(option_type))
|
||||
is_type_diagnostic_item(cx, expr_ty, sym::option_type)
|
||||
}
|
||||
|
||||
fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
|
||||
|
@ -7,6 +7,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
@ -304,7 +305,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args:
|
||||
if_chain! {
|
||||
// `.iter()` call
|
||||
if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = *iter;
|
||||
if iter_path.ident.name == sym!(iter);
|
||||
if iter_path.ident.name == sym::iter;
|
||||
// range expression in `.zip()` call: `0..x.len()`
|
||||
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
|
||||
if is_integer_const(cx, start, 0);
|
||||
|
@ -17,6 +17,7 @@ use rustc_middle::ty::{self, fold::TypeVisitor, Ty};
|
||||
use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::{BytePos, Span};
|
||||
use rustc_span::sym;
|
||||
use std::convert::TryFrom;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
@ -115,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
|
||||
let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
|
||||
|| match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD)
|
||||
|| (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD)
|
||||
&& is_type_diagnostic_item(cx, arg_ty, sym!(string_type)));
|
||||
&& is_type_diagnostic_item(cx, arg_ty, sym::string_type));
|
||||
|
||||
let from_deref = !from_borrow
|
||||
&& (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF)
|
||||
@ -518,7 +519,10 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
|
||||
self.possible_borrower.add(borrowed.local, lhs);
|
||||
},
|
||||
other => {
|
||||
if ContainsRegion.visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty).is_continue() {
|
||||
if ContainsRegion
|
||||
.visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty)
|
||||
.is_continue()
|
||||
{
|
||||
return;
|
||||
}
|
||||
rvalue_locals(other, |rhs| {
|
||||
|
66
clippy_lints/src/ref_option_ref.rs
Normal file
66
clippy_lints/src/ref_option_ref.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types.
|
||||
@ -65,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
|
||||
format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::string_type) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
|
@ -9,6 +9,7 @@ use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
|
||||
use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then};
|
||||
|
||||
@ -141,7 +142,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
|
||||
}
|
||||
|
||||
fn attr_is_cfg(attr: &Attribute) -> bool {
|
||||
attr.meta_item_list().is_some() && attr.has_name(sym!(cfg))
|
||||
attr.meta_item_list().is_some() && attr.has_name(sym::cfg)
|
||||
}
|
||||
|
||||
fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {
|
||||
|
@ -4,6 +4,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::sym;
|
||||
|
||||
use if_chain::if_chain;
|
||||
|
||||
@ -154,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd {
|
||||
}
|
||||
|
||||
fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym!(string_type))
|
||||
is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::string_type)
|
||||
}
|
||||
|
||||
fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
|
||||
|
@ -8,6 +8,7 @@ use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for manual swapping.
|
||||
@ -197,7 +198,7 @@ fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<
|
||||
|
||||
if matches!(ty.kind(), ty::Slice(_))
|
||||
|| matches!(ty.kind(), ty::Array(_, _))
|
||||
|| is_type_diagnostic_item(cx, ty, sym!(vec_type))
|
||||
|| is_type_diagnostic_item(cx, ty, sym::vec_type)
|
||||
|| is_type_diagnostic_item(cx, ty, sym!(vecdeque_type))
|
||||
{
|
||||
return Slice::Swappable(lhs1, idx1, idx2);
|
||||
|
@ -9,6 +9,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usages of `Err(x)?`.
|
||||
@ -133,7 +134,7 @@ fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> O
|
||||
fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let ty::Adt(_, subst) = ty.kind();
|
||||
if is_type_diagnostic_item(cx, ty, sym!(result_type));
|
||||
if is_type_diagnostic_item(cx, ty, sym::result_type);
|
||||
let err_ty = subst.type_at(1);
|
||||
then {
|
||||
Some(err_ty)
|
||||
@ -151,7 +152,7 @@ fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<
|
||||
let ready_ty = subst.type_at(0);
|
||||
|
||||
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
|
||||
if cx.tcx.is_diagnostic_item(sym!(result_type), ready_def.did);
|
||||
if cx.tcx.is_diagnostic_item(sym::result_type, ready_def.did);
|
||||
let err_ty = ready_subst.type_at(1);
|
||||
|
||||
then {
|
||||
@ -170,11 +171,11 @@ fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) ->
|
||||
let ready_ty = subst.type_at(0);
|
||||
|
||||
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
|
||||
if cx.tcx.is_diagnostic_item(sym!(option_type), ready_def.did);
|
||||
if cx.tcx.is_diagnostic_item(sym::option_type, ready_def.did);
|
||||
let some_ty = ready_subst.type_at(0);
|
||||
|
||||
if let ty::Adt(some_def, some_subst) = some_ty.kind();
|
||||
if cx.tcx.is_diagnostic_item(sym!(result_type), some_def.did);
|
||||
if cx.tcx.is_diagnostic_item(sym::result_type, some_def.did);
|
||||
let err_ty = some_subst.type_at(1);
|
||||
|
||||
then {
|
||||
|
@ -10,9 +10,9 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{
|
||||
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
|
||||
ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind,
|
||||
TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
|
||||
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId,
|
||||
ImplItem, ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt,
|
||||
StmtKind, SyntheticTyParamKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
@ -522,7 +522,7 @@ impl Types {
|
||||
);
|
||||
return; // don't recurse into the type
|
||||
}
|
||||
} else if cx.tcx.is_diagnostic_item(sym!(vec_type), def_id) {
|
||||
} else if cx.tcx.is_diagnostic_item(sym::vec_type, def_id) {
|
||||
if_chain! {
|
||||
// Get the _ part of Vec<_>
|
||||
if let Some(ref last) = last_path_segment(qpath).args;
|
||||
@ -559,7 +559,7 @@ impl Types {
|
||||
return; // don't recurse into the type
|
||||
}
|
||||
}
|
||||
} else if cx.tcx.is_diagnostic_item(sym!(option_type), def_id) {
|
||||
} else if cx.tcx.is_diagnostic_item(sym::option_type, def_id) {
|
||||
if match_type_parameter(cx, qpath, &paths::OPTION).is_some() {
|
||||
span_lint(
|
||||
cx,
|
||||
@ -678,17 +678,30 @@ impl Types {
|
||||
// details.
|
||||
return;
|
||||
}
|
||||
|
||||
// When trait objects or opaque types have lifetime or auto-trait bounds,
|
||||
// we need to add parentheses to avoid a syntax error due to its ambiguity.
|
||||
// Originally reported as the issue #3128.
|
||||
let inner_snippet = snippet(cx, inner.span, "..");
|
||||
let suggestion = match &inner.kind {
|
||||
TyKind::TraitObject(bounds, lt_bound) if bounds.len() > 1 || !lt_bound.is_elided() => {
|
||||
format!("&{}({})", ltopt, &inner_snippet)
|
||||
},
|
||||
TyKind::Path(qpath)
|
||||
if get_bounds_if_impl_trait(cx, qpath, inner.hir_id)
|
||||
.map_or(false, |bounds| bounds.len() > 1) =>
|
||||
{
|
||||
format!("&{}({})", ltopt, &inner_snippet)
|
||||
},
|
||||
_ => format!("&{}{}", ltopt, &inner_snippet),
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BORROWED_BOX,
|
||||
hir_ty.span,
|
||||
"you seem to be trying to use `&Box<T>`. Consider using just `&T`",
|
||||
"try",
|
||||
format!(
|
||||
"&{}{}",
|
||||
ltopt,
|
||||
&snippet(cx, inner.span, "..")
|
||||
),
|
||||
suggestion,
|
||||
// To make this `MachineApplicable`, at least one needs to check if it isn't a trait item
|
||||
// because the trait impls of it will break otherwise;
|
||||
// and there may be other cases that result in invalid code.
|
||||
@ -721,6 +734,21 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> {
|
||||
if_chain! {
|
||||
if let Some(did) = qpath_res(cx, qpath, id).opt_def_id();
|
||||
if let Some(node) = cx.tcx.hir().get_if_local(did);
|
||||
if let Node::GenericParam(generic_param) = node;
|
||||
if let GenericParamKind::Type { synthetic, .. } = generic_param.kind;
|
||||
if synthetic == Some(SyntheticTyParamKind::ImplTrait);
|
||||
then {
|
||||
Some(generic_param.bounds)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for binding a unit value.
|
||||
///
|
||||
@ -1582,7 +1610,7 @@ fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||
if names.is_empty() {
|
||||
return false;
|
||||
}
|
||||
if names[0] == sym!(libc) || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) {
|
||||
if names[0] == sym::libc || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -2749,7 +2777,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
|
||||
}
|
||||
|
||||
if match_path(ty_path, &paths::HASHMAP) {
|
||||
if method.ident.name == sym!(new) {
|
||||
if method.ident.name == sym::new {
|
||||
self.suggestions
|
||||
.insert(e.span, "HashMap::default()".to_string());
|
||||
} else if method.ident.name == sym!(with_capacity) {
|
||||
@ -2762,7 +2790,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
|
||||
);
|
||||
}
|
||||
} else if match_path(ty_path, &paths::HASHSET) {
|
||||
if method.ident.name == sym!(new) {
|
||||
if method.ident.name == sym::new {
|
||||
self.suggestions
|
||||
.insert(e.span, "HashSet::default()".to_string());
|
||||
} else if method.ident.name == sym!(with_capacity) {
|
||||
|
@ -6,6 +6,7 @@ use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegme
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, subst::GenericArgKind};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -175,7 +176,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
|
||||
if let name = name_ident.ident.name.to_ident_string();
|
||||
if name == "sort_by" || name == "sort_unstable_by";
|
||||
if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args;
|
||||
if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym!(vec_type));
|
||||
if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::vec_type);
|
||||
if let closure_body = cx.tcx.hir().body(*closure_body_id);
|
||||
if let &[
|
||||
Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
|
||||
|
@ -123,6 +123,17 @@ fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
|
||||
fn_source
|
||||
.rfind("->")
|
||||
.map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
|
||||
let mut rpos = rpos;
|
||||
let chars: Vec<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)]
|
||||
ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
|
||||
|
@ -11,6 +11,7 @@ use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.
|
||||
@ -92,11 +93,11 @@ fn collect_unwrap_info<'tcx>(
|
||||
invert: bool,
|
||||
) -> Vec<UnwrapInfo<'tcx>> {
|
||||
fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool {
|
||||
is_type_diagnostic_item(cx, ty, sym!(option_type)) && ["is_some", "is_none"].contains(&method_name)
|
||||
is_type_diagnostic_item(cx, ty, sym::option_type) && ["is_some", "is_none"].contains(&method_name)
|
||||
}
|
||||
|
||||
fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool {
|
||||
is_type_diagnostic_item(cx, ty, sym!(result_type)) && ["is_ok", "is_err"].contains(&method_name)
|
||||
is_type_diagnostic_item(cx, ty, sym::result_type) && ["is_ok", "is_err"].contains(&method_name)
|
||||
}
|
||||
|
||||
if let ExprKind::Binary(op, left, right) = &expr.kind {
|
||||
@ -168,8 +169,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(ref method_name, _, ref args, _) = expr.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].kind;
|
||||
if [sym!(unwrap), sym!(unwrap_err)].contains(&method_name.ident.name);
|
||||
let call_to_unwrap = method_name.ident.name == sym!(unwrap);
|
||||
if [sym::unwrap, sym!(unwrap_err)].contains(&method_name.ident.name);
|
||||
let call_to_unwrap = method_name.ident.name == sym::unwrap;
|
||||
if let Some(unwrappable) = self.unwrappables.iter()
|
||||
.find(|u| u.ident.res == path.res);
|
||||
// Span contexts should not differ with the conditional branch
|
||||
|
@ -5,7 +5,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()`
|
||||
@ -57,8 +57,8 @@ impl<'tcx> LateLintPass<'tcx> for UnwrapInResult {
|
||||
// first check if it's a method or function
|
||||
if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind;
|
||||
// checking if its return type is `result` or `option`
|
||||
if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type))
|
||||
|| is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type));
|
||||
if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::result_type)
|
||||
|| is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::option_type);
|
||||
then {
|
||||
lint_impl_body(cx, impl_item.span, impl_item);
|
||||
}
|
||||
@ -82,8 +82,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
|
||||
// check for `expect`
|
||||
if let Some(arglists) = method_chain_args(expr, &["expect"]) {
|
||||
let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
|
||||
if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
|
||||
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
|
||||
if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type)
|
||||
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type)
|
||||
{
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
@ -92,8 +92,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
|
||||
// check for `unwrap`
|
||||
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
|
||||
let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
|
||||
if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
|
||||
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
|
||||
if is_type_diagnostic_item(self.lcx, reciever_ty, sym::option_type)
|
||||
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym::result_type)
|
||||
{
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use rustc_hir::{Expr, ExprKind, HirId, MatchSource};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, TyS};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`,`IntoIter` calls
|
||||
@ -106,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
||||
if_chain! {
|
||||
let a = cx.typeck_results().expr_ty(e);
|
||||
let b = cx.typeck_results().expr_ty(&args[0]);
|
||||
if is_type_diagnostic_item(cx, a, sym!(result_type));
|
||||
if is_type_diagnostic_item(cx, a, sym::result_type);
|
||||
if let ty::Adt(_, substs) = a.kind();
|
||||
if let Some(a_type) = substs.types().next();
|
||||
if TyS::same_type(a_type, b);
|
||||
@ -136,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
||||
then {
|
||||
if_chain! {
|
||||
if match_def_path(cx, def_id, &paths::TRY_FROM);
|
||||
if is_type_diagnostic_item(cx, a, sym!(result_type));
|
||||
if is_type_diagnostic_item(cx, a, sym::result_type);
|
||||
if let ty::Adt(_, substs) = a.kind();
|
||||
if let Some(a_type) = substs.types().next();
|
||||
if TyS::same_type(a_type, b);
|
||||
|
@ -225,7 +225,7 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust,ignore
|
||||
/// utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))
|
||||
/// utils::is_type_diagnostic_item(cx, ty, sym::vec_type)
|
||||
/// ```
|
||||
pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
|
||||
internal,
|
||||
@ -724,7 +724,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
|
||||
expr.span,
|
||||
"usage of `utils::match_type()` on a type diagnostic item",
|
||||
"try",
|
||||
format!("utils::is_type_diagnostic_item({}, {}, sym!({}))", cx_snippet, ty_snippet, item_name),
|
||||
format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
|
||||
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::original_sp;
|
||||
use rustc_span::sym as rustc_sym;
|
||||
use rustc_span::symbol::{self, kw, Symbol};
|
||||
use rustc_span::{BytePos, Pos, Span, DUMMY_SP};
|
||||
use rustc_target::abi::Integer;
|
||||
@ -974,7 +975,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
|
||||
/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
|
||||
/// implementations have.
|
||||
pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
|
||||
attrs.iter().any(|attr| attr.has_name(sym!(automatically_derived)))
|
||||
attrs.iter().any(|attr| attr.has_name(rustc_sym::automatically_derived))
|
||||
}
|
||||
|
||||
/// Remove blocks around an expression.
|
||||
|
@ -44,6 +44,7 @@ pub const FN: [&str; 3] = ["core", "ops", "Fn"];
|
||||
pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"];
|
||||
pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"];
|
||||
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
|
||||
pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
|
||||
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
|
||||
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
|
||||
pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
|
||||
@ -52,6 +53,7 @@ pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entr
|
||||
pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
|
||||
pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
|
||||
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
|
||||
pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
|
||||
pub const INTO: [&str; 3] = ["core", "convert", "Into"];
|
||||
pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
|
||||
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
|
||||
|
@ -10,8 +10,7 @@ use rustc_lexer::unescape::{self, EscapeError};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_parse::parser;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{BytePos, Span};
|
||||
use rustc_span::{sym, BytePos, Span, Symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** This lint warns when you use `println!("")` to
|
||||
@ -224,7 +223,7 @@ impl EarlyLintPass for Write {
|
||||
.expect("path has at least one segment")
|
||||
.ident
|
||||
.name;
|
||||
if trait_name == sym!(Debug) {
|
||||
if trait_name == sym::Debug {
|
||||
self.in_debug_impl = true;
|
||||
}
|
||||
}
|
||||
|
@ -311,7 +311,7 @@ Running our UI test should now produce output that contains the lint message.
|
||||
According to [the rustc-dev-guide], the text should be matter of fact and avoid
|
||||
capitalization and periods, unless multiple sentences are needed.
|
||||
When code or an identifier must appear in a message or label, it should be
|
||||
surrounded with single acute accents \`.
|
||||
surrounded with single grave accents \`.
|
||||
|
||||
[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
|
||||
[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs
|
||||
|
@ -298,6 +298,13 @@ vec![
|
||||
deprecation: None,
|
||||
module: "comparison_chain",
|
||||
},
|
||||
Lint {
|
||||
name: "comparison_to_empty",
|
||||
group: "style",
|
||||
desc: "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead",
|
||||
deprecation: None,
|
||||
module: "len_zero",
|
||||
},
|
||||
Lint {
|
||||
name: "copy_iterator",
|
||||
group: "pedantic",
|
||||
@ -352,7 +359,7 @@ vec![
|
||||
group: "pedantic",
|
||||
desc: "checks for literal calls to `Default::default()`",
|
||||
deprecation: None,
|
||||
module: "default_trait_access",
|
||||
module: "default",
|
||||
},
|
||||
Lint {
|
||||
name: "deprecated_cfg_attr",
|
||||
@ -620,6 +627,13 @@ vec![
|
||||
deprecation: None,
|
||||
module: "fallible_impl_from",
|
||||
},
|
||||
Lint {
|
||||
name: "field_reassign_with_default",
|
||||
group: "style",
|
||||
desc: "binding initialized with Default should have its fields set in the initializer",
|
||||
deprecation: None,
|
||||
module: "default",
|
||||
},
|
||||
Lint {
|
||||
name: "filetype_is_file",
|
||||
group: "restriction",
|
||||
@ -746,6 +760,13 @@ vec![
|
||||
deprecation: None,
|
||||
module: "drop_forget_ref",
|
||||
},
|
||||
Lint {
|
||||
name: "from_iter_instead_of_collect",
|
||||
group: "style",
|
||||
desc: "use `.collect()` instead of `::from_iter()`",
|
||||
deprecation: None,
|
||||
module: "methods",
|
||||
},
|
||||
Lint {
|
||||
name: "future_not_send",
|
||||
group: "nursery",
|
||||
@ -1173,6 +1194,13 @@ vec![
|
||||
deprecation: None,
|
||||
module: "manual_non_exhaustive",
|
||||
},
|
||||
Lint {
|
||||
name: "manual_ok_or",
|
||||
group: "pedantic",
|
||||
desc: "finds patterns that can be encoded more concisely with `Option::ok_or`",
|
||||
deprecation: None,
|
||||
module: "manual_ok_or",
|
||||
},
|
||||
Lint {
|
||||
name: "manual_range_contains",
|
||||
group: "style",
|
||||
@ -1222,6 +1250,13 @@ vec![
|
||||
deprecation: None,
|
||||
module: "map_clone",
|
||||
},
|
||||
Lint {
|
||||
name: "map_collect_result_unit",
|
||||
group: "style",
|
||||
desc: "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`",
|
||||
deprecation: None,
|
||||
module: "methods",
|
||||
},
|
||||
Lint {
|
||||
name: "map_entry",
|
||||
group: "perf",
|
||||
@ -2013,6 +2048,13 @@ vec![
|
||||
deprecation: None,
|
||||
module: "reference",
|
||||
},
|
||||
Lint {
|
||||
name: "ref_option_ref",
|
||||
group: "pedantic",
|
||||
desc: "use `Option<&T>` instead of `&Option<&T>`",
|
||||
deprecation: None,
|
||||
module: "ref_option_ref",
|
||||
},
|
||||
Lint {
|
||||
name: "repeat_once",
|
||||
group: "complexity",
|
||||
@ -2133,16 +2175,16 @@ vec![
|
||||
module: "non_expressive_names",
|
||||
},
|
||||
Lint {
|
||||
name: "single_char_pattern",
|
||||
group: "perf",
|
||||
desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`",
|
||||
name: "single_char_add_str",
|
||||
group: "style",
|
||||
desc: "`push_str()` or `insert_str()` used with a single-character string literal as parameter",
|
||||
deprecation: None,
|
||||
module: "methods",
|
||||
},
|
||||
Lint {
|
||||
name: "single_char_push_str",
|
||||
group: "style",
|
||||
desc: "`push_str()` used with a single-character string literal as parameter",
|
||||
name: "single_char_pattern",
|
||||
group: "perf",
|
||||
desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`",
|
||||
deprecation: None,
|
||||
module: "methods",
|
||||
},
|
||||
|
@ -56,3 +56,17 @@ macro_rules! option_env_unwrap_external {
|
||||
option_env!($env).expect($message)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ref_arg_binding {
|
||||
() => {
|
||||
let ref _y = 42;
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ref_arg_function {
|
||||
() => {
|
||||
fn fun_example(ref _x: usize) {}
|
||||
};
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
#![allow(unused_variables)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
pub fn test1(foo: &mut Box<bool>) {
|
||||
// Although this function could be changed to "&mut bool",
|
||||
// avoiding the Box, mutable references to boxes are not
|
||||
@ -89,6 +91,20 @@ pub fn test13(boxed_slice: &mut Box<[i32]>) {
|
||||
*boxed_slice = data.into_boxed_slice();
|
||||
}
|
||||
|
||||
// The suggestion should include proper parentheses to avoid a syntax error.
|
||||
pub fn test14(_display: &Box<dyn Display>) {}
|
||||
pub fn test15(_display: &Box<dyn Display + Send>) {}
|
||||
pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
|
||||
|
||||
pub fn test17(_display: &Box<impl Display>) {}
|
||||
pub fn test18(_display: &Box<impl Display + Send>) {}
|
||||
pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
|
||||
|
||||
// This exists only to check what happens when parentheses are already present.
|
||||
// Even though the current implementation doesn't put extra parentheses,
|
||||
// it's fine that unnecessary parentheses appear in the future for some reason.
|
||||
pub fn test20(_display: &Box<(dyn Display + Send)>) {}
|
||||
|
||||
fn main() {
|
||||
test1(&mut Box::new(false));
|
||||
test2();
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
|
||||
--> $DIR/borrow_box.rs:19:14
|
||||
--> $DIR/borrow_box.rs:21:14
|
||||
|
|
||||
LL | let foo: &Box<bool>;
|
||||
| ^^^^^^^^^^ help: try: `&bool`
|
||||
@ -11,16 +11,58 @@ LL | #![deny(clippy::borrowed_box)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
|
||||
--> $DIR/borrow_box.rs:23:10
|
||||
--> $DIR/borrow_box.rs:25:10
|
||||
|
|
||||
LL | foo: &'a Box<bool>,
|
||||
| ^^^^^^^^^^^^^ help: try: `&'a bool`
|
||||
|
||||
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
|
||||
--> $DIR/borrow_box.rs:27:17
|
||||
--> $DIR/borrow_box.rs:29:17
|
||||
|
|
||||
LL | fn test4(a: &Box<bool>);
|
||||
| ^^^^^^^^^^ help: try: `&bool`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
|
||||
--> $DIR/borrow_box.rs:95:25
|
||||
|
|
||||
LL | pub fn test14(_display: &Box<dyn Display>) {}
|
||||
| ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display`
|
||||
|
||||
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
|
||||
--> $DIR/borrow_box.rs:96:25
|
||||
|
|
||||
LL | pub fn test15(_display: &Box<dyn Display + Send>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`
|
||||
|
||||
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
|
||||
--> $DIR/borrow_box.rs:97:29
|
||||
|
|
||||
LL | pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)`
|
||||
|
||||
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
|
||||
--> $DIR/borrow_box.rs:99:25
|
||||
|
|
||||
LL | pub fn test17(_display: &Box<impl Display>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display`
|
||||
|
||||
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
|
||||
--> $DIR/borrow_box.rs:100:25
|
||||
|
|
||||
LL | pub fn test18(_display: &Box<impl Display + Send>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)`
|
||||
|
||||
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
|
||||
--> $DIR/borrow_box.rs:101:29
|
||||
|
|
||||
LL | pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)`
|
||||
|
||||
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
|
||||
--> $DIR/borrow_box.rs:106:25
|
||||
|
|
||||
LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
|
23
tests/ui/comparison_to_empty.fixed
Normal file
23
tests/ui/comparison_to_empty.fixed
Normal 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];
|
||||
}
|
23
tests/ui/comparison_to_empty.rs
Normal file
23
tests/ui/comparison_to_empty.rs
Normal 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];
|
||||
}
|
28
tests/ui/comparison_to_empty.stderr
Normal file
28
tests/ui/comparison_to_empty.stderr
Normal 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
|
||||
|
16
tests/ui/crashes/ice-6250.rs
Normal file
16
tests/ui/crashes/ice-6250.rs
Normal 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!()
|
||||
}
|
||||
}
|
||||
}
|
27
tests/ui/crashes/ice-6250.stderr
Normal file
27
tests/ui/crashes/ice-6250.stderr
Normal 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`.
|
6
tests/ui/crashes/ice-6251.rs
Normal file
6
tests/ui/crashes/ice-6251.rs
Normal 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()
|
||||
}
|
43
tests/ui/crashes/ice-6251.stderr
Normal file
43
tests/ui/crashes/ice-6251.stderr
Normal 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`.
|
15
tests/ui/crashes/ice-6252.rs
Normal file
15
tests/ui/crashes/ice-6252.rs
Normal 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];
|
||||
}
|
46
tests/ui/crashes/ice-6252.stderr
Normal file
46
tests/ui/crashes/ice-6252.stderr
Normal 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`.
|
15
tests/ui/crashes/ice-6254.rs
Normal file
15
tests/ui/crashes/ice-6254.rs
Normal 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(_) => {},
|
||||
}
|
||||
}
|
12
tests/ui/crashes/ice-6254.stderr
Normal file
12
tests/ui/crashes/ice-6254.stderr
Normal 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
|
||||
|
15
tests/ui/crashes/ice-6255.rs
Normal file
15
tests/ui/crashes/ice-6255.rs
Normal 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!();
|
13
tests/ui/crashes/ice-6255.stderr
Normal file
13
tests/ui/crashes/ice-6255.stderr
Normal 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
|
||||
|
13
tests/ui/crashes/ice-6256.rs
Normal file
13
tests/ui/crashes/ice-6256.rs
Normal 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
|
||||
}
|
18
tests/ui/crashes/ice-6256.stderr
Normal file
18
tests/ui/crashes/ice-6256.stderr
Normal 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`.
|
110
tests/ui/field_reassign_with_default.rs
Normal file
110
tests/ui/field_reassign_with_default.rs
Normal 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();
|
||||
}
|
75
tests/ui/field_reassign_with_default.stderr
Normal file
75
tests/ui/field_reassign_with_default.stderr
Normal 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
|
||||
|
@ -3,9 +3,6 @@
|
||||
fn main() {
|
||||
let a = ["1", "lol", "3", "NaN", "5"];
|
||||
|
||||
let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
|
||||
assert_eq!(element, Some(1));
|
||||
|
||||
#[rustfmt::skip]
|
||||
let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
|
||||
.into_iter()
|
||||
|
@ -1,14 +1,5 @@
|
||||
error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead.
|
||||
--> $DIR/filter_map_next.rs:6:32
|
||||
|
|
||||
LL | let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::filter-map-next` implied by `-D warnings`
|
||||
= note: replace `filter_map(|s| s.parse().ok()).next()` with `find_map(|s| s.parse().ok())`
|
||||
|
||||
error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead.
|
||||
--> $DIR/filter_map_next.rs:10:26
|
||||
error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead.
|
||||
--> $DIR/filter_map_next.rs:7:26
|
||||
|
|
||||
LL | let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
|
||||
| __________________________^
|
||||
@ -19,6 +10,8 @@ LL | | if x == 2 {
|
||||
LL | | })
|
||||
LL | | .next();
|
||||
| |_______________^
|
||||
|
|
||||
= note: `-D clippy::filter-map-next` implied by `-D warnings`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to previous error
|
||||
|
||||
|
10
tests/ui/filter_map_next_fixable.fixed
Normal file
10
tests/ui/filter_map_next_fixable.fixed
Normal 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));
|
||||
}
|
10
tests/ui/filter_map_next_fixable.rs
Normal file
10
tests/ui/filter_map_next_fixable.rs
Normal 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));
|
||||
}
|
10
tests/ui/filter_map_next_fixable.stderr
Normal file
10
tests/ui/filter_map_next_fixable.stderr
Normal 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
|
||||
|
@ -1,4 +1,4 @@
|
||||
error: called `filter(p).map(q)` on an `Iterator`
|
||||
error: called `filter(..).map(..)` on an `Iterator`
|
||||
--> $DIR/filter_methods.rs:5:21
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect();
|
||||
@ -7,7 +7,7 @@ LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x *
|
||||
= note: `-D clippy::filter-map` implied by `-D warnings`
|
||||
= help: this is more succinctly expressed by calling `.filter_map(..)` instead
|
||||
|
||||
error: called `filter(p).flat_map(q)` on an `Iterator`
|
||||
error: called `filter(..).flat_map(..)` on an `Iterator`
|
||||
--> $DIR/filter_methods.rs:7:21
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6]
|
||||
@ -19,7 +19,7 @@ LL | | .flat_map(|x| x.checked_mul(2))
|
||||
|
|
||||
= help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
|
||||
|
||||
error: called `filter_map(p).flat_map(q)` on an `Iterator`
|
||||
error: called `filter_map(..).flat_map(..)` on an `Iterator`
|
||||
--> $DIR/filter_methods.rs:13:21
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6]
|
||||
@ -31,7 +31,7 @@ LL | | .flat_map(|x| x.checked_mul(2))
|
||||
|
|
||||
= help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
|
||||
|
||||
error: called `filter_map(p).map(q)` on an `Iterator`
|
||||
error: called `filter_map(..).map(..)` on an `Iterator`
|
||||
--> $DIR/filter_methods.rs:19:21
|
||||
|
|
||||
LL | let _: Vec<_> = vec![5_i8; 6]
|
||||
|
@ -1,4 +1,4 @@
|
||||
error: called `find(p).map(q)` on an `Iterator`
|
||||
error: called `find(..).map(..)` on an `Iterator`
|
||||
--> $DIR/find_map.rs:20:26
|
||||
|
|
||||
LL | let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap());
|
||||
@ -7,7 +7,7 @@ LL | let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s
|
||||
= note: `-D clippy::find-map` implied by `-D warnings`
|
||||
= help: this is more succinctly expressed by calling `.find_map(..)` instead
|
||||
|
||||
error: called `find(p).map(q)` on an `Iterator`
|
||||
error: called `find(..).map(..)` on an `Iterator`
|
||||
--> $DIR/find_map.rs:23:29
|
||||
|
|
||||
LL | let _: Option<Flavor> = desserts_of_the_week
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user