[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
+[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
- [Finding something to fix/improve](#finding-something-to-fiximprove)
- [Writing code](#writing-code)
- [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
+ - [IntelliJ Rust](#intellij-rust)
+ - [Rust Analyzer](#rust-analyzer)
- [How Clippy works](#how-clippy-works)
- - [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust)
+ - [Syncing changes between Clippy and [`rust-lang/rust`]](#syncing-changes-between-clippy-and-rust-langrust)
- [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos)
- - [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
- - [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust)
+ - [Performing the sync from [`rust-lang/rust`] to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
+ - [Performing the sync from Clippy to [`rust-lang/rust`]](#performing-the-sync-from-clippy-to-rust-langrust)
- [Defining remotes](#defining-remotes)
- [Issue and PR triage](#issue-and-pr-triage)
- [Bors and Homu](#bors-and-homu)
## Getting code-completion for rustc internals to work
-Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals
+### IntelliJ Rust
+Unfortunately, [`IntelliJ Rust`][IntelliJ_rust_homepage] does not (yet?) understand how Clippy uses compiler-internals
using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not
available via a `rustup` component at the time of writing.
To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via
`git clone https://github.com/rust-lang/rust/`.
Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
-which rust-analyzer will be able to understand.
-Run `cargo dev ra_setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo
+which `IntelliJ Rust` will be able to understand.
+Run `cargo dev ide_setup --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
you just cloned.
The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses.
Just make sure to remove the dependencies again before finally making a pull request!
-[ra_homepage]: https://rust-analyzer.github.io/
[rustc_repo]: https://github.com/rust-lang/rust/
+[IntelliJ_rust_homepage]: https://intellij-rust.github.io/
+
+### Rust Analyzer
+As of [#6869][6869], [`rust-analyzer`][ra_homepage] can understand that Clippy uses compiler-internals
+using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippys `Cargo.toml.`
+You will required a `nightly` toolchain with the `rustc-dev` component installed.
+Make sure that in the `rust-analyzer` configuration, you set
+```
+{ "rust-analyzer.rustcSource": "discover" }
+```
+and
+```
+{ "rust-analyzer.updates.channel": "nightly" }
+```
+You should be able to see information on things like `Expr` or `EarlyContext` now if you hover them, also
+a lot more type hints.
+This will work with `rust-analyzer 2021-03-15` shipped in nightly `1.52.0-nightly (107896c32 2021-03-15)` or later.
+
+[ra_homepage]: https://rust-analyzer.github.io/
+[6869]: https://github.com/rust-lang/rust-clippy/pull/6869
## How Clippy works
[package]
name = "clippy"
-version = "0.1.52"
+version = "0.1.53"
authors = ["The Rust Clippy Developers"]
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
serde = { version = "1.0", features = ["derive"] }
derive-new = "0.5"
regex = "1.4"
+quote = "1"
+syn = { version = "1", features = ["full"] }
# A noop dependency that changes in the Rust repository, it's a bit of a hack.
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
},
CliError::RaSetupActive => {
eprintln!(
- "error: a local rustc repo is enabled as path dependency via `cargo dev ra_setup`.
+ "error: a local rustc repo is enabled as path dependency via `cargo dev ide_setup`.
Not formatting because that would format the local repo as well!
Please revert the changes to Cargo.tomls first."
);
--- /dev/null
+use std::fs;
+use std::fs::File;
+use std::io::prelude::*;
+use std::path::{Path, PathBuf};
+
+// This module takes an absolute path to a rustc repo and alters the dependencies to point towards
+// the respective rustc subcrates instead of using extern crate xyz.
+// This allows rust analyzer to analyze rustc internals and show proper information inside clippy
+// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details
+
+/// # Panics
+///
+/// Panics if `rustc_path` does not lead to a rustc repo or the files could not be read
+pub fn run(rustc_path: Option<&str>) {
+ // we can unwrap here because the arg is required by clap
+ let rustc_path = PathBuf::from(rustc_path.unwrap())
+ .canonicalize()
+ .expect("failed to get the absolute repo path");
+ assert!(rustc_path.is_dir(), "path is not a directory");
+ let rustc_source_basedir = rustc_path.join("compiler");
+ assert!(
+ rustc_source_basedir.is_dir(),
+ "are you sure the path leads to a rustc repo?"
+ );
+
+ let clippy_root_manifest = fs::read_to_string("Cargo.toml").expect("failed to read ./Cargo.toml");
+ let clippy_root_lib_rs = fs::read_to_string("src/driver.rs").expect("failed to read ./src/driver.rs");
+ inject_deps_into_manifest(
+ &rustc_source_basedir,
+ "Cargo.toml",
+ &clippy_root_manifest,
+ &clippy_root_lib_rs,
+ )
+ .expect("Failed to inject deps into ./Cargo.toml");
+
+ let clippy_lints_manifest =
+ fs::read_to_string("clippy_lints/Cargo.toml").expect("failed to read ./clippy_lints/Cargo.toml");
+ let clippy_lints_lib_rs =
+ fs::read_to_string("clippy_lints/src/lib.rs").expect("failed to read ./clippy_lints/src/lib.rs");
+ inject_deps_into_manifest(
+ &rustc_source_basedir,
+ "clippy_lints/Cargo.toml",
+ &clippy_lints_manifest,
+ &clippy_lints_lib_rs,
+ )
+ .expect("Failed to inject deps into ./clippy_lints/Cargo.toml");
+}
+
+fn inject_deps_into_manifest(
+ rustc_source_dir: &Path,
+ manifest_path: &str,
+ 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 ide_setup: warning: deps already found inside {}, doing nothing.",
+ manifest_path
+ );
+ return Ok(());
+ }
+
+ let extern_crates = lib_rs
+ .lines()
+ // get the deps
+ .filter(|line| line.starts_with("extern crate"))
+ // we have something like "extern crate foo;", we only care about the "foo"
+ // ↓ ↓
+ // extern crate rustc_middle;
+ .map(|s| &s[13..(s.len() - 1)]);
+
+ let new_deps = extern_crates.map(|dep| {
+ // format the dependencies that are going to be put inside the Cargo.toml
+ format!(
+ "{dep} = {{ path = \"{source_path}/{dep}\" }}\n",
+ dep = dep,
+ source_path = rustc_source_dir.display()
+ )
+ });
+
+ // format a new [dependencies]-block with the new deps we need to inject
+ let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
+ new_deps.for_each(|dep_line| {
+ all_deps.push_str(&dep_line);
+ });
+ all_deps.push_str("\n[dependencies]\n");
+
+ // replace "[dependencies]" with
+ // [dependencies]
+ // dep1 = { path = ... }
+ // dep2 = { path = ... }
+ // etc
+ let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);
+
+ // println!("{}", new_manifest);
+ let mut file = File::create(manifest_path)?;
+ file.write_all(new_manifest.as_bytes())?;
+
+ println!("Dependency paths injected: {}", manifest_path);
+
+ Ok(())
+}
pub mod bless;
pub mod fmt;
+pub mod ide_setup;
pub mod new_lint;
-pub mod ra_setup;
pub mod serve;
pub mod stderr_length_check;
pub mod update_lints;
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
use clap::{App, Arg, ArgMatches, SubCommand};
-use clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints};
+use clippy_dev::{bless, fmt, ide_setup, new_lint, serve, stderr_length_check, update_lints};
fn main() {
let matches = get_clap_config();
("limit_stderr_length", _) => {
stderr_length_check::check();
},
- ("ra_setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")),
+ ("ide_setup", Some(matches)) => ide_setup::run(matches.value_of("rustc-repo-path")),
("serve", Some(matches)) => {
let port = matches.value_of("port").unwrap().parse().unwrap();
let lint = matches.value_of("lint");
.about("Ensures that stderr files do not grow longer than a certain amount of lines."),
)
.subcommand(
- SubCommand::with_name("ra_setup")
- .about("Alter dependencies so rust-analyzer can find rustc internals")
+ SubCommand::with_name("ide_setup")
+ .about("Alter dependencies so Intellij Rust can find rustc internals")
.arg(
Arg::with_name("rustc-repo-path")
.long("repo-path")
+++ /dev/null
-use std::fs;
-use std::fs::File;
-use std::io::prelude::*;
-use std::path::{Path, PathBuf};
-
-// This module takes an absolute path to a rustc repo and alters the dependencies to point towards
-// the respective rustc subcrates instead of using extern crate xyz.
-// This allows rust analyzer to analyze rustc internals and show proper information inside clippy
-// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details
-
-/// # Panics
-///
-/// Panics if `rustc_path` does not lead to a rustc repo or the files could not be read
-pub fn run(rustc_path: Option<&str>) {
- // we can unwrap here because the arg is required by clap
- let rustc_path = PathBuf::from(rustc_path.unwrap())
- .canonicalize()
- .expect("failed to get the absolute repo path");
- assert!(rustc_path.is_dir(), "path is not a directory");
- let rustc_source_basedir = rustc_path.join("compiler");
- assert!(
- rustc_source_basedir.is_dir(),
- "are you sure the path leads to a rustc repo?"
- );
-
- let clippy_root_manifest = fs::read_to_string("Cargo.toml").expect("failed to read ./Cargo.toml");
- let clippy_root_lib_rs = fs::read_to_string("src/driver.rs").expect("failed to read ./src/driver.rs");
- inject_deps_into_manifest(
- &rustc_source_basedir,
- "Cargo.toml",
- &clippy_root_manifest,
- &clippy_root_lib_rs,
- )
- .expect("Failed to inject deps into ./Cargo.toml");
-
- let clippy_lints_manifest =
- fs::read_to_string("clippy_lints/Cargo.toml").expect("failed to read ./clippy_lints/Cargo.toml");
- let clippy_lints_lib_rs =
- fs::read_to_string("clippy_lints/src/lib.rs").expect("failed to read ./clippy_lints/src/lib.rs");
- inject_deps_into_manifest(
- &rustc_source_basedir,
- "clippy_lints/Cargo.toml",
- &clippy_lints_manifest,
- &clippy_lints_lib_rs,
- )
- .expect("Failed to inject deps into ./clippy_lints/Cargo.toml");
-}
-
-fn inject_deps_into_manifest(
- rustc_source_dir: &Path,
- manifest_path: &str,
- 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
- .filter(|line| line.starts_with("extern crate"))
- // we have something like "extern crate foo;", we only care about the "foo"
- // ↓ ↓
- // extern crate rustc_middle;
- .map(|s| &s[13..(s.len() - 1)]);
-
- let new_deps = extern_crates.map(|dep| {
- // format the dependencies that are going to be put inside the Cargo.toml
- format!(
- "{dep} = {{ path = \"{source_path}/{dep}\" }}\n",
- dep = dep,
- source_path = rustc_source_dir.display()
- )
- });
-
- // format a new [dependencies]-block with the new deps we need to inject
- let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
- new_deps.for_each(|dep_line| {
- all_deps.push_str(&dep_line);
- });
- all_deps.push_str("\n[dependencies]\n");
-
- // replace "[dependencies]" with
- // [dependencies]
- // dep1 = { path = ... }
- // dep2 = { path = ... }
- // etc
- let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);
-
- // println!("{}", new_manifest);
- let mut file = File::create(manifest_path)?;
- file.write_all(new_manifest.as_bytes())?;
-
- println!("Dependency paths injected: {}", manifest_path);
-
- Ok(())
-}
[package]
name = "clippy_lints"
# begin automatic update
-version = "0.1.52"
+version = "0.1.53"
# end automatic update
authors = ["The Rust Clippy Developers"]
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
quine-mc_cluskey = "0.2.2"
regex-syntax = "0.6"
serde = { version = "1.0", features = ["derive"] }
-smallvec = { version = "1", features = ["union"] }
toml = "0.5.3"
unicode-normalization = "0.1"
semver = "0.11"
# NOTE: cargo requires serde feat in its url dep
# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
url = { version = "2.1.0", features = ["serde"] }
-quote = "1"
-syn = { version = "1", features = ["full"] }
[features]
deny-warnings = []
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use crate::consts::constant_simple;
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::span_lint_and_help;
-
declare_clippy_lint! {
/// **What it does:** Checks for usage of `as` conversions.
///
use std::fmt;
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions};
use rustc_lint::{EarlyContext, EarlyLintPass, Lint};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use crate::consts::{constant, Constant};
-use crate::utils::{is_direct_expn_of, is_expn_of, match_panic_call, snippet_opt, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
-use crate::utils::{
- eq_expr_value, get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method,
-};
-use crate::utils::{higher, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{eq_expr_value, get_trait_def_id, trait_ref_of_method};
+use clippy_utils::{higher, paths, sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
$($trait_name:ident),+) => {
match $op {
$(hir::BinOpKind::$trait_name => {
- let [krate, module] = crate::utils::paths::OPS_MODULE;
+ let [krate, module] = paths::OPS_MODULE;
let path: [&str; 3] = [krate, module, concat!(stringify!($trait_name), "Assign")];
let trait_id = if let Some(trait_id) = get_trait_def_id($cx, &path) {
trait_id
-use crate::utils::{implements_trait, snippet, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::implements_trait;
use rustc_errors::Applicability;
use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
-use crate::utils::{match_def_path, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::match_def_path;
use if_chain::if_chain;
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, ExprKind};
//! checks for attributes
-use crate::utils::{
- first_line_of_span, is_present_in_source, match_panic_def_id, snippet_opt, span_lint, span_lint_and_help,
- span_lint_and_sugg, span_lint_and_then, without_block_comments,
-};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::match_panic_def_id;
+use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
use if_chain::if_chain;
use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
use rustc_errors::Applicability;
|stmt| match &stmt.kind {
StmtKind::Local(_) => true,
StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
- _ => false,
+ StmtKind::Item(_) => false,
},
)
}
// check for `rustfmt_skip` and `rustfmt::skip`
if let Some(skip_item) = &items[1].meta_item();
if skip_item.has_name(sym!(rustfmt_skip)) ||
- skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym!(skip);
+ skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym::skip;
// Only lint outer attributes, because custom inner attributes are unstable
// Tracking issue: https://github.com/rust-lang/rust/issues/54726
if let AttrStyle::Outer = attr.style;
}
}
},
- _ => {},
+ MetaItemKind::NameValue(..) => {},
}
}
}
-use crate::utils::{match_def_path, paths, span_lint_and_note};
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::{match_def_path, paths};
use rustc_hir::def_id::DefId;
use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
use rustc_lint::{LateContext, LateLintPass};
use crate::consts::{constant, Constant};
-use crate::utils::sugg::Sugg;
-use crate::utils::{span_lint, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass};
-use crate::utils::{
- differing_macro_contexts, get_parent_expr, get_trait_def_id, implements_trait, paths,
- snippet_block_with_applicability, span_lint, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::source::snippet_block_with_applicability;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{differing_macro_contexts, get_parent_expr};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_middle::hir::map::Map;
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 `if` conditions that use blocks containing an
if let Some(parent) = get_parent_expr(self.cx, expr);
if let ExprKind::MethodCall(_, _, args, _) = parent.kind;
let caller = self.cx.typeck_results().expr_ty(&args[0]);
- if let Some(iter_id) = get_trait_def_id(self.cx, &paths::ITERATOR);
+ if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator);
if implements_trait(self.cx, caller, iter_id, &[]);
then {
return;
-use crate::utils::{
- eq_expr_value, get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt,
- span_lint_and_sugg, span_lint_and_then,
-};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::{eq_expr_value, get_trait_def_id, in_macro, paths};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use crate::utils::{
- contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::match_type;
+use clippy_utils::{contains_name, get_pat_name, paths, single_segment_path};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp};
use std::path::PathBuf;
-use crate::utils::{run_lints, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::run_lints;
use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind, PathSegment};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_constant;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::is_isize_or_usize;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, FloatTy, Ty};
-use crate::utils::{in_constant, is_isize_or_usize, snippet_opt, span_lint_and_sugg};
-
use super::{utils, CAST_LOSSLESS};
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_isize_or_usize;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, FloatTy, Ty};
-use crate::utils::{is_isize_or_usize, span_lint};
-
use super::{utils, CAST_POSSIBLE_TRUNCATION};
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_isize_or_usize;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::Ty;
-use crate::utils::{is_isize_or_usize, span_lint};
-
use super::{utils, CAST_POSSIBLE_WRAP};
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_isize_or_usize;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, FloatTy, Ty};
-use crate::utils::{is_isize_or_usize, span_lint};
-
use super::{utils, CAST_PRECISION_LOSS};
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_hir_ty_cfg_dependant;
+use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, GenericArg};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::sym;
use rustc_target::abi::LayoutOf;
-use if_chain::if_chain;
-
-use crate::utils::{is_hir_ty_cfg_dependant, span_lint};
-
use super::CAST_PTR_ALIGNMENT;
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+use clippy_utils::diagnostics::span_lint;
+use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, MutTy, Mutability, TyKind, UnOp};
use rustc_lint::LateContext;
use rustc_middle::ty;
-use if_chain::if_chain;
-
-use crate::utils::span_lint;
-
use super::CAST_REF_TO_MUT;
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+use crate::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{method_chain_args, sext};
+use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
-use if_chain::if_chain;
-
-use crate::consts::{constant, Constant};
-use crate::utils::{method_chain_args, sext, span_lint};
-
use super::CAST_SIGN_LOSS;
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_with_applicability;
+use if_chain::if_chain;
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, UintTy};
-use if_chain::if_chain;
-
-use crate::utils::{snippet_with_applicability, span_lint_and_then};
-
use super::CHAR_LIT_AS_U8;
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty, UintTy};
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
-
use super::{utils, FN_TO_NUMERIC_CAST};
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
-
use super::{utils, FN_TO_NUMERIC_CAST_WITH_TRUNCATION};
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
mod unnecessary_cast;
mod utils;
+use clippy_utils::is_hir_ty_cfg_dependant;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use crate::utils::is_hir_ty_cfg_dependant;
-
declare_clippy_lint! {
/// **What it does:** Checks for casts from any numerical to a float type where
/// the receiving type cannot store all values from the original type without
use std::borrow::Cow;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::meets_msrv;
+use clippy_utils::sugg::Sugg;
+use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, TypeAndMut};
use rustc_semver::RustcVersion;
-use if_chain::if_chain;
-
-use crate::utils::sugg::Sugg;
-use crate::utils::{meets_msrv, span_lint_and_sugg};
-
use super::PTR_AS_PTR;
const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0);
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::numeric_literal::NumericLiteral;
+use clippy_utils::source::snippet_opt;
+use if_chain::if_chain;
use rustc_ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Lit, UnOp};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, FloatTy, InferTy, Ty};
-use if_chain::if_chain;
-
-use crate::utils::{numeric_literal::NumericLiteral, snippet_opt, span_lint, span_lint_and_sugg};
-
use super::UNNECESSARY_CAST;
pub(super) fn check(
lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
},
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {},
+ LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_))
+ | LitKind::Float(_, LitFloatType::Suffixed(_))
+ if cast_from.kind() == cast_to.kind() =>
+ {
+ if let Some(src) = snippet_opt(cx, lit.span) {
+ let num_lit = NumericLiteral::from_lit_kind(&src, &lit.node).unwrap();
+ lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
+ }
+ },
_ => {
if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
- span_lint(
+ span_lint_and_sugg(
cx,
UNNECESSARY_CAST,
expr.span,
"casting to the same type is unnecessary (`{}` -> `{}`)",
cast_from, cast_to
),
+ "try",
+ literal_str,
+ Applicability::MachineApplicable,
);
return true;
}
//! lint on manually implemented checked conversions that could be transformed into `try_from`
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{meets_msrv, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use crate::utils::{meets_msrv, snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
-
const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0);
declare_clippy_lint! {
//! calculate cognitive complexity and warn about overly complex functions
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::LimitStack;
use rustc_ast::ast::Attribute;
use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId};
use rustc_span::source_map::Span;
use rustc_span::{sym, BytePos};
-use crate::utils::{is_type_diagnostic_item, snippet_opt, span_lint_and_help, LimitStack};
-
declare_clippy_lint! {
/// **What it does:** Checks for methods with high cognitive complexity.
///
//!
//! This lint is **warn** by default
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::{snippet_block, snippet_block_with_applicability};
+use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_ast::ast;
+use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::sugg::Sugg;
-use crate::utils::{snippet_block, snippet_block_with_applicability, span_lint_and_sugg, span_lint_and_then};
-use rustc_errors::Applicability;
-
declare_clippy_lint! {
/// **What it does:** Checks for nested `if` statements which can be collapsed
/// by `&&`-combining their conditions.
-use crate::utils::visitors::LocalUsedVisitor;
-use crate::utils::{path_to_local, span_lint_and_then, SpanlessEq};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::visitors::LocalUsedVisitor;
+use clippy_utils::{path_to_local, SpanlessEq};
use if_chain::if_chain;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp};
-use crate::utils::{
- get_trait_def_id, if_sequence, implements_trait, parent_node_is_if_expr, paths, span_lint_and_help, SpanlessEq,
-};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, SpanlessEq};
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash};
-use crate::utils::{get_parent_expr, if_sequence, span_lint_and_note};
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash};
+use clippy_utils::{get_parent_expr, if_sequence};
use rustc_hir::{Block, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{is_copy, match_path, paths, span_lint_and_note};
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::ty::is_copy;
use rustc_hir::{Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+use if_chain::if_chain;
declare_clippy_lint! {
/// **What it does:** Checks for types that implement `Copy` as well as
impl<'tcx> LateLintPass<'tcx> for CopyIterator {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
- if let ItemKind::Impl(Impl {
- of_trait: Some(ref trait_ref),
- ..
- }) = item.kind
- {
+ if_chain! {
+ if let ItemKind::Impl(Impl {
+ of_trait: Some(ref trait_ref),
+ ..
+ }) = item.kind;
let ty = cx.tcx.type_of(item.def_id);
-
- if is_copy(cx, ty) && match_path(&trait_ref.path, &paths::ITERATOR) {
+ if is_copy(cx, ty);
+ if let Some(trait_id) = trait_ref.trait_def_id();
+ if cx.tcx.is_diagnostic_item(sym::Iterator, trait_id);
+ then {
span_lint_and_note(
cx,
COPY_ITERATOR,
-use crate::utils::{match_def_path, paths, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
-use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::snippet_opt;
use rustc_ast::ast;
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::Applicability;
-use crate::utils::{
- any_parent_is_automatically_derived, contains_name, match_def_path, paths, snippet_with_macro_callsite,
-};
-use crate::utils::{span_lint_and_note, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
+use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
}
}
+ #[allow(clippy::too_many_lines)]
fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
// start from the `let mut _ = _::default();` and look at all the following
// statements, see if they re-assign the fields of the binding
.collect::<Vec<String>>()
.join(", ");
+ // give correct suggestion if generics are involved (see #6944)
+ let binding_type = if_chain! {
+ if let ty::Adt(adt_def, substs) = binding_type.kind();
+ if !substs.is_empty();
+ let adt_def_ty_name = cx.tcx.item_name(adt_def.did);
+ let generic_args = substs.iter().collect::<Vec<_>>();
+ let tys_str = generic_args
+ .iter()
+ .map(ToString::to_string)
+ .collect::<Vec<_>>()
+ .join(", ");
+ then {
+ format!("{}::<{}>", adt_def_ty_name, &tys_str)
+ } else {
+ binding_type.to_string()
+ }
+ };
+
let sugg = if ext_with_default {
if field_list.is_empty() {
format!("{}::default()", binding_type)
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use if_chain::if_chain;
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use if_chain::if_chain;
-
-use crate::utils::{snippet, span_lint_and_sugg};
-
declare_clippy_lint! {
/// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type
/// inference.
-use crate::utils::{get_parent_expr, implements_trait, snippet, span_lint_and_sugg};
-use if_chain::if_chain;
-use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX, PREC_PREFIX};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::ty::peel_mid_ty_refs;
+use clippy_utils::{get_parent_node, in_macro, is_allowed};
+use rustc_ast::util::parser::PREC_PREFIX;
use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
+use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{symbol::sym, Span};
declare_clippy_lint! {
/// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.
"Explicit use of deref or deref_mut method while not in a method chain."
}
-declare_lint_pass!(Dereferencing => [
- EXPLICIT_DEREF_METHODS
+impl_lint_pass!(Dereferencing => [
+ EXPLICIT_DEREF_METHODS,
]);
+#[derive(Default)]
+pub struct Dereferencing {
+ state: Option<(State, StateData)>,
+
+ // While parsing a `deref` method call in ufcs form, the path to the function is itself an
+ // expression. This is to store the id of that expression so it can be skipped when
+ // `check_expr` is called for it.
+ skip_expr: Option<HirId>,
+}
+
+struct StateData {
+ /// Span of the top level expression
+ span: Span,
+ /// The required mutability
+ target_mut: Mutability,
+}
+
+enum State {
+ // Any number of deref method calls.
+ DerefMethod {
+ // The number of calls in a sequence which changed the referenced type
+ ty_changed_count: usize,
+ is_final_ufcs: bool,
+ },
+}
+
+// A reference operation considered by this lint pass
+enum RefOp {
+ Method(Mutability),
+ Deref,
+ AddrOf,
+}
+
impl<'tcx> LateLintPass<'tcx> for Dereferencing {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if_chain! {
- if !expr.span.from_expansion();
- if let ExprKind::MethodCall(ref method_name, _, ref args, _) = &expr.kind;
- if args.len() == 1;
-
- then {
- if let Some(parent_expr) = get_parent_expr(cx, expr) {
- // Check if we have the whole call chain here
- if let ExprKind::MethodCall(..) = parent_expr.kind {
- return;
- }
- // Check for Expr that we don't want to be linted
- let precedence = parent_expr.precedence();
- match precedence {
- // Lint a Call is ok though
- ExprPrecedence::Call | ExprPrecedence::AddrOf => (),
- _ => {
- if precedence.order() >= PREC_PREFIX && precedence.order() <= PREC_POSTFIX {
- return;
- }
- }
+ // Skip path expressions from deref calls. e.g. `Deref::deref(e)`
+ if Some(expr.hir_id) == self.skip_expr.take() {
+ return;
+ }
+
+ // Stop processing sub expressions when a macro call is seen
+ if in_macro(expr.span) {
+ if let Some((state, data)) = self.state.take() {
+ report(cx, expr, state, data);
+ }
+ return;
+ }
+
+ let typeck = cx.typeck_results();
+ let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) {
+ x
+ } else {
+ // The whole chain of reference operations has been seen
+ if let Some((state, data)) = self.state.take() {
+ report(cx, expr, state, data);
+ }
+ return;
+ };
+
+ match (self.state.take(), kind) {
+ (None, kind) => {
+ let parent = get_parent_node(cx.tcx, expr.hir_id);
+ let expr_ty = typeck.expr_ty(expr);
+
+ match kind {
+ RefOp::Method(target_mut)
+ if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
+ && is_linted_explicit_deref_position(parent, expr.hir_id, expr.span) =>
+ {
+ self.state = Some((
+ State::DerefMethod {
+ ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) {
+ 0
+ } else {
+ 1
+ },
+ is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
+ },
+ StateData {
+ span: expr.span,
+ target_mut,
+ },
+ ));
}
+ _ => (),
}
- let name = method_name.ident.as_str();
- lint_deref(cx, &*name, &args[0], args[0].span, expr.span);
- }
+ },
+ (Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method(_)) => {
+ self.state = Some((
+ State::DerefMethod {
+ ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
+ ty_changed_count
+ } else {
+ ty_changed_count + 1
+ },
+ is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
+ },
+ data,
+ ));
+ },
+
+ (Some((state, data)), _) => report(cx, expr, state, data),
}
}
}
-fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) {
- match method_name {
- "deref" => {
- let impls_deref_trait = cx.tcx.lang_items().deref_trait().map_or(false, |id| {
- implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[])
- });
- if impls_deref_trait {
- span_lint_and_sugg(
- cx,
- EXPLICIT_DEREF_METHODS,
- expr_span,
- "explicit deref method call",
- "try this",
- format!("&*{}", &snippet(cx, var_span, "..")),
- Applicability::MachineApplicable,
- );
- }
+fn try_parse_ref_op(
+ tcx: TyCtxt<'tcx>,
+ typeck: &'tcx TypeckResults<'_>,
+ expr: &'tcx Expr<'_>,
+) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
+ let (def_id, arg) = match expr.kind {
+ ExprKind::MethodCall(_, _, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
+ ExprKind::Call(
+ Expr {
+ kind: ExprKind::Path(path),
+ hir_id,
+ ..
+ },
+ [arg],
+ ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
+ ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
+ return Some((RefOp::Deref, sub_expr));
},
- "deref_mut" => {
- let impls_deref_mut_trait = cx.tcx.lang_items().deref_mut_trait().map_or(false, |id| {
- implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[])
- });
- if impls_deref_mut_trait {
- span_lint_and_sugg(
- cx,
- EXPLICIT_DEREF_METHODS,
- expr_span,
- "explicit deref_mut method call",
- "try this",
- format!("&mut *{}", &snippet(cx, var_span, "..")),
- Applicability::MachineApplicable,
- );
- }
+ ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)),
+ _ => return None,
+ };
+ if tcx.is_diagnostic_item(sym::deref_method, def_id) {
+ Some((RefOp::Method(Mutability::Not), arg))
+ } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
+ Some((RefOp::Method(Mutability::Mut), arg))
+ } else {
+ None
+ }
+}
+
+// Checks whether the type for a deref call actually changed the type, not just the mutability of
+// the reference.
+fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
+ match (result_ty.kind(), arg_ty.kind()) {
+ (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty),
+
+ // The result type for a deref method is always a reference
+ // Not matching the previous pattern means the argument type is not a reference
+ // This means that the type did change
+ _ => false,
+ }
+}
+
+// Checks whether the parent node is a suitable context for switching from a deref method to the
+// deref operator.
+fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId, child_span: Span) -> bool {
+ let parent = match parent {
+ Some(Node::Expr(e)) if e.span.ctxt() == child_span.ctxt() => e,
+ _ => return true,
+ };
+ match parent.kind {
+ // Leave deref calls in the middle of a method chain.
+ // e.g. x.deref().foo()
+ ExprKind::MethodCall(_, _, [self_arg, ..], _) if self_arg.hir_id == child_id => false,
+
+ // Leave deref calls resulting in a called function
+ // e.g. (x.deref())()
+ ExprKind::Call(func_expr, _) if func_expr.hir_id == child_id => false,
+
+ // Makes an ugly suggestion
+ // e.g. *x.deref() => *&*x
+ ExprKind::Unary(UnOp::Deref, _)
+ // Postfix expressions would require parens
+ | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
+ | ExprKind::Field(..)
+ | ExprKind::Index(..)
+ | ExprKind::Err => false,
+
+ ExprKind::Box(..)
+ | ExprKind::ConstBlock(..)
+ | ExprKind::Array(_)
+ | ExprKind::Call(..)
+ | ExprKind::MethodCall(..)
+ | ExprKind::Tup(..)
+ | ExprKind::Binary(..)
+ | ExprKind::Unary(..)
+ | ExprKind::Lit(..)
+ | ExprKind::Cast(..)
+ | ExprKind::Type(..)
+ | ExprKind::DropTemps(..)
+ | ExprKind::If(..)
+ | ExprKind::Loop(..)
+ | ExprKind::Match(..)
+ | ExprKind::Closure(..)
+ | ExprKind::Block(..)
+ | ExprKind::Assign(..)
+ | ExprKind::AssignOp(..)
+ | ExprKind::Path(..)
+ | ExprKind::AddrOf(..)
+ | ExprKind::Break(..)
+ | ExprKind::Continue(..)
+ | ExprKind::Ret(..)
+ | ExprKind::InlineAsm(..)
+ | ExprKind::LlvmInlineAsm(..)
+ | ExprKind::Struct(..)
+ | ExprKind::Repeat(..)
+ | ExprKind::Yield(..) => true,
+ }
+}
+
+#[allow(clippy::needless_pass_by_value)]
+fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
+ match state {
+ State::DerefMethod {
+ ty_changed_count,
+ is_final_ufcs,
+ } => {
+ let mut app = Applicability::MachineApplicable;
+ let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
+ let ty = cx.typeck_results().expr_ty(expr);
+ let (_, ref_count) = peel_mid_ty_refs(ty);
+ let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
+ // a deref call changing &T -> &U requires two deref operators the first time
+ // this occurs. One to remove the reference, a second to call the deref impl.
+ "*".repeat(ty_changed_count + 1)
+ } else {
+ "*".repeat(ty_changed_count)
+ };
+ let addr_of_str = if ty_changed_count < ref_count {
+ // Check if a reborrow from &mut T -> &T is required.
+ if data.target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
+ "&*"
+ } else {
+ ""
+ }
+ } else if data.target_mut == Mutability::Mut {
+ "&mut "
+ } else {
+ "&"
+ };
+
+ let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
+ format!("({})", expr_str)
+ } else {
+ expr_str.into_owned()
+ };
+
+ span_lint_and_sugg(
+ cx,
+ EXPLICIT_DEREF_METHODS,
+ data.span,
+ match data.target_mut {
+ Mutability::Not => "explicit `deref` method call",
+ Mutability::Mut => "explicit `deref_mut` method call",
+ },
+ "try this",
+ format!("{}{}{}", addr_of_str, deref_str, expr_str),
+ app,
+ );
},
- _ => (),
}
}
-use crate::utils::paths;
-use crate::utils::{
- get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_def_path, span_lint_and_help,
- span_lint_and_note, span_lint_and_then,
-};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then};
+use clippy_utils::paths;
+use clippy_utils::ty::is_copy;
+use clippy_utils::{get_trait_def_id, is_allowed, is_automatically_derived, match_def_path};
use if_chain::if_chain;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor};
-use crate::utils::{fn_def_id, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::fn_def_id;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::Expr;
-use crate::utils::{
- implements_trait, is_entrypoint_fn, is_expn_of, is_type_diagnostic_item, match_panic_def_id, method_chain_args,
- return_ty, span_lint, span_lint_and_note,
-};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
use if_chain::if_chain;
use itertools::Itertools;
use rustc_ast::ast::{Async, AttrKind, Attribute, FnKind, FnRetTy, ItemKind};
let returns_nothing = match &sig.decl.output {
FnRetTy::Default(..) => true,
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
- _ => false,
+ FnRetTy::Ty(_) => false,
};
if returns_nothing && !is_async && !block.stmts.is_empty() {
//! Lint on unnecessary double comparisons. Some examples:
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::eq_expr_value;
+use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
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::Span;
-use crate::utils::{eq_expr_value, snippet_with_applicability, span_lint_and_sugg};
-
declare_clippy_lint! {
/// **What it does:** Checks for double comparisons that could be simplified to a single expression.
///
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use rustc_ast::ast::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{is_copy, match_def_path, paths, span_lint_and_note};
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::ty::is_copy;
+use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::match_type;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_span::source_map::Spanned;
use crate::consts::{constant, Constant};
-use crate::utils::paths;
-use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::paths;
declare_clippy_lint! {
/// **What it does:** Checks for calculation of subsecond microseconds or milliseconds
//! Lint on if expressions with an else if, but without a final else branch.
+use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::span_lint_and_help;
-
declare_clippy_lint! {
/// **What it does:** Checks for usage of if expressions with an `else if` branch,
/// but without a final `else` branch.
//! lint when there is an enum with no variants
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::SpanlessEq;
-use crate::utils::{get_item_name, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt};
-use crate::utils::{snippet_with_applicability, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
+use clippy_utils::SpanlessEq;
+use clippy_utils::{get_item_name, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
//! don't fit into an `i32`
use crate::consts::{miri_to_const, Constant};
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::util::IntTypeExt;
//! lint on enum variants that are prefixed or suffixed by the same characters
-use crate::utils::{camel_case, is_present_in_source};
-use crate::utils::{span_lint, span_lint_and_help};
+use clippy_utils::camel_case;
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::source::is_present_in_source;
use rustc_ast::ast::{EnumDef, Item, ItemKind, VisibilityKind};
use rustc_lint::{EarlyContext, EarlyLintPass, Lint};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use crate::utils::{
- ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of,
- multispan_sugg, snippet, span_lint, span_lint_and_then,
-};
+use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::ty::{implements_trait, is_copy};
+use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind};
+use clippy_utils::diagnostics::span_lint;
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::Span;
use crate::consts::{constant_simple, Constant};
-use crate::utils::span_lint;
declare_clippy_lint! {
/// **What it does:** Checks for erasing operations, e.g., `x * 0`.
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::contains_ty;
use rustc_hir::intravisit;
use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_target::spec::abi::Abi;
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
-use crate::utils::{contains_ty, span_lint};
-
#[derive(Copy, Clone)]
pub struct BoxedLocal {
pub too_large_for_stack: u64,
}
}
- fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) { }
+ fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
}
impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::higher;
+use clippy_utils::higher::VecArgs;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::{implements_trait, type_is_unsafe_function};
+use clippy_utils::{is_adjusted, iter_input_pats};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath};
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{
- implements_trait, is_adjusted, iter_input_pats, snippet_opt, span_lint_and_sugg, span_lint_and_then,
- type_is_unsafe_function,
-};
-use clippy_utils::higher;
-use clippy_utils::higher::VecArgs;
-
declare_clippy_lint! {
/// **What it does:** Checks for closures which just call another function where
/// the function can be called directly. `unsafe` functions or calls where types
-use crate::utils::{get_parent_expr, path_to_local, path_to_local_id, span_lint, span_lint_and_note};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
+use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
.init
.as_ref()
.map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),
- _ => StopEarly::KeepGoing,
+ StmtKind::Item(..) => StopEarly::KeepGoing,
}
}
-use crate::utils::{attr_by_name, in_macro, match_path_ast, span_lint_and_help};
-use rustc_ast::ast::{
- AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind,
-};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{attr_by_name, in_macro, match_path_ast};
+use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
-use crate::utils::{indent_of, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::indent_of;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
-use crate::utils::{is_entrypoint_fn, match_def_path, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{is_entrypoint_fn, match_def_path, paths};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
-use crate::utils::{is_expn_of, match_function_call, paths, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::{is_expn_of, match_function_call, paths};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use crate::utils::{is_expn_of, is_type_diagnostic_item, match_panic_def_id, method_chain_args, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_expn_of, match_panic_def_id, method_chain_args};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
-use crate::utils::{match_def_path, paths, span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{match_def_path, paths, sugg};
use if_chain::if_chain;
use rustc_ast::util::parser::AssocOp;
use rustc_errors::Applicability;
-use crate::utils::{numeric_literal, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::numeric_literal;
use if_chain::if_chain;
use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_errors::Applicability;
constant, constant_simple, Constant,
Constant::{Int, F32, F64},
};
-use crate::utils::{eq_expr_value, get_parent_expr, numeric_literal, span_lint_and_sugg, sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
-use crate::utils::paths;
-use crate::utils::{
- is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, snippet_opt,
- span_lint_and_then,
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::paths;
+use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use crate::utils::{differing_macro_contexts, snippet_opt, span_lint_and_help, span_lint_and_note};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
+use clippy_utils::differing_macro_contexts;
+use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
use rustc_lint::{EarlyContext, EarlyLintPass};
-use crate::utils::paths::INTO;
-use crate::utils::{match_def_path, meets_msrv, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::paths::INTO;
+use clippy_utils::{match_def_path, meets_msrv};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{def, Expr, ExprKind, PrimTy, QPath, TyKind};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
-use crate::utils::is_type_diagnostic_item;
-use crate::utils::span_lint_and_sugg;
-use crate::utils::sugg::Sugg;
-
declare_clippy_lint! {
/// **What it does:**
/// Checks for function invocations of the form `primitive::from_str_radix(s, 10)`
-use crate::utils::{
- attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats,
- match_def_path, must_use_attr, path_to_local, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help,
- span_lint_and_then, trait_ref_of_method, type_is_unsafe_function,
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then};
+use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function};
+use clippy_utils::{
+ attr_by_name, attrs::is_proc_macro, is_trait_impl_item, iter_input_pats, match_def_path, must_use_attr,
+ path_to_local, return_ty, trait_ref_of_method,
};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
-use crate::utils;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::return_ty;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl, HirId};
use rustc_infer::infer::TyCtxtInferExt;
if let FnKind::Closure = kind {
return;
}
- let ret_ty = utils::return_ty(cx, hir_id);
+ let ret_ty = return_ty(cx, hir_id);
if let Opaque(id, subst) = *ret_ty.kind() {
let preds = cx.tcx.explicit_item_bounds(id);
let mut is_future = false;
fulfillment_cx.select_all_or_error(&infcx)
});
if let Err(send_errors) = send_result {
- utils::span_lint_and_then(
+ span_lint_and_then(
cx,
FUTURE_NOT_SEND,
span,
//! lint on using `x.get(x.len() - 1)` instead of `x.last()`
-use crate::utils::{is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::SpanlessEq;
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
+use clippy_utils::source::snippet;
use if_chain::if_chain;
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_span::source_map::Span;
use crate::consts::{constant_simple, Constant};
-use crate::utils::{clip, snippet, span_lint, unsext};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{clip, unsext};
declare_clippy_lint! {
/// **What it does:** Checks for identity operations, e.g., `x + 0`.
-use crate::utils::{is_type_diagnostic_item, span_lint_and_help, SpanlessEq};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::SpanlessEq;
use if_chain::if_chain;
use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor};
use rustc_hir::{Expr, ExprKind, MatchSource};
-use crate::utils::{is_type_diagnostic_item, method_chain_args, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::method_chain_args;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath};
//! lint on if branches that could be swapped so no `!` operation is necessary
//! on the condition
+use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::span_lint_and_help;
-
declare_clippy_lint! {
/// **What it does:** Checks for usage of `!` or `!=` in an if condition with an
/// else branch.
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::{match_qpath, meets_msrv, parent_node_is_if_expr};
+use if_chain::if_chain;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+const IF_THEN_SOME_ELSE_NONE_MSRV: RustcVersion = RustcVersion::new(1, 50, 0);
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for if-else that could be written to `bool::then`.
+ ///
+ /// **Why is this bad?** Looks a little redundant. Using `bool::then` helps it have less lines of code.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// # let v = vec![0];
+ /// let a = if v.is_empty() {
+ /// println!("true!");
+ /// Some(42)
+ /// } else {
+ /// None
+ /// };
+ /// ```
+ ///
+ /// Could be written:
+ ///
+ /// ```rust
+ /// # let v = vec![0];
+ /// let a = v.is_empty().then(|| {
+ /// println!("true!");
+ /// 42
+ /// });
+ /// ```
+ pub IF_THEN_SOME_ELSE_NONE,
+ restriction,
+ "Finds if-else that could be written using `bool::then`"
+}
+
+pub struct IfThenSomeElseNone {
+ msrv: Option<RustcVersion>,
+}
+
+impl IfThenSomeElseNone {
+ #[must_use]
+ pub fn new(msrv: Option<RustcVersion>) -> Self {
+ Self { msrv }
+ }
+}
+
+impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
+
+impl LateLintPass<'_> for IfThenSomeElseNone {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
+ if !meets_msrv(self.msrv.as_ref(), &IF_THEN_SOME_ELSE_NONE_MSRV) {
+ return;
+ }
+
+ if in_external_macro(cx.sess(), expr.span) {
+ return;
+ }
+
+ // We only care about the top-most `if` in the chain
+ if parent_node_is_if_expr(expr, cx) {
+ return;
+ }
+
+ if_chain! {
+ if let ExprKind::If(ref cond, ref then, Some(ref els)) = expr.kind;
+ if let ExprKind::Block(ref then_block, _) = then.kind;
+ if let Some(ref then_expr) = then_block.expr;
+ if let ExprKind::Call(ref then_call, [then_arg]) = then_expr.kind;
+ if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
+ if match_qpath(then_call_qpath, &clippy_utils::paths::OPTION_SOME);
+ if let ExprKind::Block(ref els_block, _) = els.kind;
+ if els_block.stmts.is_empty();
+ if let Some(ref els_expr) = els_block.expr;
+ if let ExprKind::Path(ref els_call_qpath) = els_expr.kind;
+ if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE);
+ then {
+ let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
+ let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
+ format!("({})", cond_snip)
+ } else {
+ cond_snip.into_owned()
+ };
+ let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
+ let closure_body = if then_block.stmts.is_empty() {
+ arg_snip.into_owned()
+ } else {
+ format!("{{ /* snippet */ {} }}", arg_snip)
+ };
+ let help = format!(
+ "consider using `bool::then` like: `{}.then(|| {})`",
+ cond_snip,
+ closure_body,
+ );
+ span_lint_and_help(
+ cx,
+ IF_THEN_SOME_ELSE_NONE,
+ expr.span,
+ "this could be simplified with `bool::then`",
+ None,
+ &help,
+ );
+ }
+ }
+ }
+
+ extract_msrv_attr!(LateContext);
+}
-use crate::utils::{match_panic_def_id, snippet_opt, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::match_panic_def_id;
+use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
-use crate::utils::{in_macro, match_qpath, span_lint_and_sugg, SpanlessEq};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{in_macro, match_qpath, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use if_chain::if_chain;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::{self as hir, ExprKind};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Symbol;
-use if_chain::if_chain;
-
-use crate::utils::{snippet, span_lint_and_sugg};
-
declare_clippy_lint! {
- /// **What it does:** Checks for struct constructors where the order of the field init
- /// shorthand in the constructor is inconsistent with the order in the struct definition.
+ /// **What it does:** Checks for struct constructors where all fields are shorthand and
+ /// the order of the field init shorthand in the constructor is inconsistent
+ /// with the order in the struct definition.
///
/// **Why is this bad?** Since the order of fields in a constructor doesn't affect the
/// resulted instance as the below example indicates,
/// let x = 1;
/// let y = 2;
///
- /// // This assertion never fails.
+ /// // This assertion never fails:
/// assert_eq!(Foo { x, y }, Foo { y, x });
/// ```
///
- /// inconsistent order means nothing and just decreases readability and consistency.
+ /// inconsistent order can be confusing and decreases readability and consistency.
///
/// **Known problems:** None.
///
/// }
/// let x = 1;
/// let y = 2;
+ ///
/// Foo { y, x };
/// ```
///
cx,
INCONSISTENT_STRUCT_CONSTRUCTOR,
expr.span,
- "inconsistent struct constructor",
+ "struct constructor field order is inconsistent with struct definition field order",
"try",
sugg,
Applicability::MachineApplicable,
//! lint on indexing and slicing operations
use crate::consts::{constant, Constant};
-use crate::utils::{higher, span_lint, span_lint_and_help};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::higher;
use rustc_ast::ast::RangeLimits;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::{implements_trait, match_type};
+use clippy_utils::{get_trait_def_id, higher, match_qpath, paths};
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, match_type, paths, span_lint};
-
declare_clippy_lint! {
/// **What it does:** Checks for iteration that is guaranteed to be infinite.
///
//! lint on inherent implementations
-use crate::utils::{in_macro, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::in_macro;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{def_id, Crate, Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method};
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,
- trait_ref_of_method,
-};
-
declare_clippy_lint! {
/// **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`.
///
//! checks for `#[inline]` on trait methods without bodies
-use crate::utils::span_lint_and_then;
-use crate::utils::sugg::DiagnosticBuilderExt;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg::DiagnosticBuilderExt;
use rustc_ast::ast::Attribute;
use rustc_errors::Applicability;
use rustc_hir::{TraitFn, TraitItem, TraitItemKind};
//! lint on blocks unnecessarily using >= with a + 1 or - 1
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
use rustc_ast::ast::{BinOpKind, Expr, ExprKind, Lit, LitKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{snippet_opt, span_lint_and_sugg};
-
declare_clippy_lint! {
/// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block
///
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
//! lint when items are used after statements
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use rustc_ast::ast::{Block, ItemKind, StmtKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use crate::rustc_target::abi::LayoutOf;
-use crate::utils::span_lint_and_then;
+use clippy_utils::diagnostics::span_lint_and_then;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
//! lint when there is a large size difference between variants on an enum
-use crate::utils::{snippet_opt, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind, VariantData};
use rustc_lint::{LateContext, LateLintPass};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet;
+use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::ty::{self, ConstKind};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use if_chain::if_chain;
-
use crate::rustc_target::abi::LayoutOf;
-use crate::utils::{snippet, span_lint_and_help};
declare_clippy_lint! {
/// **What it does:** Checks for local arrays that may be too large.
-use crate::utils::{
- get_item_name, get_parent_as_impl, is_allowed, snippet_with_applicability, span_lint, span_lint_and_sugg,
- span_lint_and_then,
-};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{get_item_name, get_parent_as_impl, is_allowed};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
-use crate::utils::{path_to_local_id, snippet, span_lint_and_then, visitors::LocalUsedVisitor};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::{path_to_local_id, visitors::LocalUsedVisitor};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::{is_must_use_ty, match_type};
+use clippy_utils::{is_must_use_func_call, paths};
use if_chain::if_chain;
use rustc_hir::{Local, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help};
-
declare_clippy_lint! {
/// **What it does:** Checks for `let _ = <expr>`
/// where expr is #[must_use]
extern crate rustc_trait_selection;
extern crate rustc_typeck;
-use crate::utils::parse_msrv;
+use clippy_utils::parse_msrv;
use rustc_data_structures::fx::FxHashSet;
use rustc_lint::LintId;
use rustc_session::Session;
mod if_let_mutex;
mod if_let_some_result;
mod if_not_else;
+mod if_then_some_else_none;
mod implicit_return;
mod implicit_saturating_sub;
mod inconsistent_struct_constructor;
mod undropped_manually_drops;
mod unicode;
mod unit_return_expecting_ord;
+mod unit_types;
mod unnamed_address;
mod unnecessary_sort_by;
mod unnecessary_wraps;
&if_let_mutex::IF_LET_MUTEX,
&if_let_some_result::IF_LET_SOME_RESULT,
&if_not_else::IF_NOT_ELSE,
+ &if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
&implicit_return::IMPLICIT_RETURN,
&implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
&types::BOX_VEC,
&types::IMPLICIT_HASHER,
&types::INVALID_UPCAST_COMPARISONS,
- &types::LET_UNIT_VALUE,
&types::LINKEDLIST,
&types::OPTION_OPTION,
&types::RC_BUFFER,
&types::REDUNDANT_ALLOCATION,
&types::TYPE_COMPLEXITY,
- &types::UNIT_ARG,
- &types::UNIT_CMP,
&types::VEC_BOX,
&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS,
&unicode::INVISIBLE_CHARACTERS,
&unicode::NON_ASCII_LITERAL,
&unicode::UNICODE_NOT_NFC,
&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
+ &unit_types::LET_UNIT_VALUE,
+ &unit_types::UNIT_ARG,
+ &unit_types::UNIT_CMP,
&unnamed_address::FN_ADDRESS_COMPARISONS,
&unnamed_address::VTABLE_ADDRESS_COMPARISONS,
&unnecessary_sort_by::UNNECESSARY_SORT_BY,
store.register_late_pass(|| box map_clone::MapClone);
store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
store.register_late_pass(|| box shadow::Shadow);
- store.register_late_pass(|| box types::LetUnitValue);
- store.register_late_pass(|| box types::UnitCmp);
+ store.register_late_pass(|| box unit_types::UnitTypes);
store.register_late_pass(|| box loops::Loops);
store.register_late_pass(|| box main_recursion::MainRecursion::default());
store.register_late_pass(|| box lifetimes::Lifetimes);
store.register_late_pass(|| box useless_conversion::UselessConversion::default());
store.register_late_pass(|| box types::ImplicitHasher);
store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom);
- store.register_late_pass(|| box types::UnitArg);
store.register_late_pass(|| box double_comparison::DoubleComparisons);
store.register_late_pass(|| box question_mark::QuestionMark);
store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings);
store.register_late_pass(|| box verbose_file_reads::VerboseFileReads);
store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
store.register_late_pass(|| box unnamed_address::UnnamedAddress);
- store.register_late_pass(|| box dereference::Dereferencing);
+ store.register_late_pass(|| box dereference::Dereferencing::default());
store.register_late_pass(|| box option_if_let_else::OptionIfLetElse);
store.register_late_pass(|| box future_not_send::FutureNotSend);
store.register_late_pass(|| box if_let_mutex::IfLetMutex);
store.register_late_pass(|| box redundant_slicing::RedundantSlicing);
store.register_late_pass(|| box from_str_radix_10::FromStrRadix10);
store.register_late_pass(|| box manual_map::ManualMap);
+ store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
LintId::of(&exhaustive_items::EXHAUSTIVE_STRUCTS),
LintId::of(&exit::EXIT),
LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
+ LintId::of(&if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
LintId::of(&implicit_return::IMPLICIT_RETURN),
LintId::of(&indexing_slicing::INDEXING_SLICING),
LintId::of(&inherent_impl::MULTIPLE_INHERENT_IMPL),
LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS),
LintId::of(&types::IMPLICIT_HASHER),
LintId::of(&types::INVALID_UPCAST_COMPARISONS),
- LintId::of(&types::LET_UNIT_VALUE),
LintId::of(&types::LINKEDLIST),
LintId::of(&types::OPTION_OPTION),
LintId::of(&unicode::NON_ASCII_LITERAL),
LintId::of(&unicode::UNICODE_NOT_NFC),
+ LintId::of(&unit_types::LET_UNIT_VALUE),
LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS),
LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS),
LintId::of(&unused_self::UNUSED_SELF),
LintId::of(&types::BOX_VEC),
LintId::of(&types::REDUNDANT_ALLOCATION),
LintId::of(&types::TYPE_COMPLEXITY),
- LintId::of(&types::UNIT_ARG),
- LintId::of(&types::UNIT_CMP),
LintId::of(&types::VEC_BOX),
LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
LintId::of(&unicode::INVISIBLE_CHARACTERS),
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
+ LintId::of(&unit_types::UNIT_ARG),
+ LintId::of(&unit_types::UNIT_CMP),
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(&transmute::TRANSMUTE_PTR_TO_REF),
LintId::of(&types::BORROWED_BOX),
LintId::of(&types::TYPE_COMPLEXITY),
- LintId::of(&types::UNIT_ARG),
LintId::of(&types::VEC_BOX),
+ LintId::of(&unit_types::UNIT_ARG),
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(&unwrap::UNNECESSARY_UNWRAP),
LintId::of(&useless_conversion::USELESS_CONVERSION),
LintId::of(&transmute::WRONG_TRANSMUTE),
LintId::of(&transmuting_null::TRANSMUTING_NULL),
LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
- LintId::of(&types::UNIT_CMP),
LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
LintId::of(&unicode::INVISIBLE_CHARACTERS),
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
+ LintId::of(&unit_types::UNIT_CMP),
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
-use crate::utils::{in_macro, span_lint, trait_ref_of_method};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{in_macro, trait_ref_of_method};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::intravisit::{
walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty,
//! Lints concerned with the grouping of digits with underscores in integral or
//! floating-point literal expressions.
-use crate::utils::{
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{
in_macro,
numeric_literal::{NumericLiteral, Radix},
- snippet_opt, span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
use super::EMPTY_LOOP;
-use crate::utils::{is_in_panic_handler, is_no_std_crate, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{is_in_panic_handler, is_no_std_crate};
use rustc_hir::{Block, Expr};
use rustc_lint::LateContext;
use super::{
get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP,
};
-use crate::utils::{get_enclosing_block, is_integer_const, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{get_enclosing_block, is_integer_const};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_block, walk_expr};
use super::EXPLICIT_INTO_ITER_LOOP;
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use super::EXPLICIT_ITER_LOOP;
-use crate::utils::{match_trait_method, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
+use clippy_utils::{match_trait_method, paths};
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty, TyS};
use rustc_span::sym;
-use crate::utils::{is_type_diagnostic_item, match_type, paths};
-
pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) {
let should_lint = match method_name {
"iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]),
use super::FOR_KV_MAP;
-use crate::utils::visitors::LocalUsedVisitor;
-use crate::utils::{is_type_diagnostic_item, match_type, multispan_sugg, paths, snippet, span_lint_and_then, sugg};
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
+use clippy_utils::visitors::LocalUsedVisitor;
+use clippy_utils::{paths, sugg};
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
use super::FOR_LOOPS_OVER_FALLIBLES;
-use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
use rustc_hir::{Expr, Pat};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use super::ITER_NEXT_LOOP;
-use crate::utils::{match_trait_method, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_trait_method;
use rustc_hir::Expr;
use rustc_lint::LateContext;
+use rustc_span::sym;
pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, expr: &Expr<'_>) -> bool {
- if match_trait_method(cx, arg, &paths::ITERATOR) {
+ if is_trait_method(cx, arg, sym::Iterator) {
span_lint(
cx,
ITER_NEXT_LOOP,
use super::utils::make_iterator_snippet;
use super::MANUAL_FLATTEN;
-use crate::utils::{is_ok_ctor, is_some_ctor, path_to_local_id, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{is_ok_ctor, is_some_ctor, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind};
use rustc_lint::LateContext;
+use rustc_middle::ty;
use rustc_span::source_map::Span;
/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the
// Prepare the help message
let mut applicability = Applicability::MaybeIncorrect;
let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
+ let copied = match cx.typeck_results().expr_ty(match_expr).kind() {
+ ty::Ref(_, inner, _) => match inner.kind() {
+ ty::Ref(..) => ".copied()",
+ _ => ""
+ }
+ _ => ""
+ };
span_lint_and_then(
cx,
span,
&msg,
|diag| {
- let sugg = format!("{}.flatten()", arg_snippet);
+ let sugg = format!("{}{}.flatten()", arg_snippet, copied);
diag.span_suggestion(
arg.span,
"try",
use super::{get_span_of_entire_for_loop, IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY};
-use crate::utils::sugg::Sugg;
-use crate::utils::{
- get_enclosing_block, higher, is_type_diagnostic_item, path_to_local, snippet, span_lint_and_sugg, sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg};
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_errors::Applicability;
mod while_let_loop;
mod while_let_on_iterator;
-use crate::utils::higher;
+use clippy_utils::higher;
use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use super::MUT_RANGE_BOUND;
-use crate::utils::{higher, path_to_local, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{higher, path_to_local};
use if_chain::if_chain;
use rustc_hir::{BindingAnnotation, Expr, HirId, Node, PatKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
-use rustc_middle::mir::FakeReadCause;
-use rustc_middle::ty;
+use rustc_middle::{mir::FakeReadCause, ty};
use rustc_span::source_map::Span;
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
}
}
- fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _:HirId) { }
+ fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
}
impl MutatePairDelegate<'_, '_> {
use super::NEEDLESS_COLLECT;
-use crate::utils::sugg::Sugg;
-use crate::utils::{
- is_type_diagnostic_item, match_trait_method, match_type, path_to_local_id, paths, snippet, span_lint_and_sugg,
- span_lint_and_then,
-};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
+use clippy_utils::{is_trait_method, path_to_local_id, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
if_chain! {
if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind;
- if chain_method.ident.name == sym!(collect) && match_trait_method(cx, &args[0], &paths::ITERATOR);
+ if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
if let Some(ref generic_args) = chain_method.args;
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
then {
init: Some(ref init_expr), .. }
) = stmt.kind;
if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind;
- if method_name.ident.name == sym!(collect) && match_trait_method(cx, &init_expr, &paths::ITERATOR);
+ if method_name.ident.name == sym!(collect) && is_trait_method(cx, &init_expr, sym::Iterator);
if let Some(ref generic_args) = method_name.args;
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
if let ty = cx.typeck_results().node_type(ty.hir_id);
use super::NEEDLESS_RANGE_LOOP;
-use crate::utils::visitors::LocalUsedVisitor;
-use crate::utils::{
- contains_name, has_iter_method, higher, is_integer_const, match_trait_method, multispan_sugg, path_to_local_id,
- paths, snippet, span_lint_and_then, sugg, SpanlessEq,
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::ty::has_iter_method;
+use clippy_utils::visitors::LocalUsedVisitor;
+use clippy_utils::{
+ contains_name, higher, is_integer_const, match_trait_method, path_to_local_id, paths, sugg, SpanlessEq,
};
use if_chain::if_chain;
use rustc_ast::ast;
use super::NEVER_LOOP;
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Stmt, StmtKind};
use rustc_lint::LateContext;
use std::iter::{once, Iterator};
match stmt.kind {
StmtKind::Semi(ref e, ..) | StmtKind::Expr(ref e, ..) => Some(e),
StmtKind::Local(ref local) => local.init.as_deref(),
- _ => None,
+ StmtKind::Item(..) => None,
}
}
use super::SAME_ITEM_PUSH;
-use crate::utils::{implements_trait, is_type_diagnostic_item, snippet_with_macro_callsite, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use if_chain::if_chain;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use super::{get_span_of_entire_for_loop, SINGLE_ELEMENT_LOOP};
-use crate::utils::{indent_of, single_segment_path, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::single_segment_path;
+use clippy_utils::source::{indent_of, snippet};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind};
-use crate::utils::{
- get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, is_integer_const, path_to_local,
- path_to_local_id, paths, sugg,
-};
+use clippy_utils::ty::{has_iter_method, implements_trait};
+use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::source_map::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{sym, Symbol};
use std::iter::Iterator;
#[derive(Debug, PartialEq)]
/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
/// actual `Iterator` that the loop uses.
pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String {
- let impls_iterator = get_trait_def_id(cx, &paths::ITERATOR).map_or(false, |id| {
+ let impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[])
});
if impls_iterator {
use super::WHILE_IMMUTABLE_CONDITION;
use crate::consts::constant;
-use crate::utils::span_lint_and_then;
-use crate::utils::usage::mutated_variables;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::usage::mutated_variables;
use if_chain::if_chain;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def::{DefKind, Res};
use super::WHILE_LET_LOOP;
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind, MatchSource, StmtKind};
use rustc_lint::{LateContext, LintContext};
use super::utils::{LoopNestVisitor, Nesting};
use super::WHILE_LET_ON_ITERATOR;
-use crate::utils::usage::mutated_variables;
-use crate::utils::{
- get_enclosing_block, get_trait_def_id, implements_trait, is_refutable, last_path_segment, match_trait_method,
- path_to_local, path_to_local_id, paths, snippet_with_applicability, span_lint_and_sugg,
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::usage::mutated_variables;
+use clippy_utils::{
+ get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, HirId, MatchSource, Node, PatKind};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
-
use rustc_span::symbol::sym;
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// Don't lint when the iterator is recreated on every iteration
if_chain! {
if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind;
- if let Some(iter_def_id) = get_trait_def_id(cx, &paths::ITERATOR);
+ if let Some(iter_def_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
if implements_trait(cx, cx.typeck_results().expr_ty(iter_expr), iter_def_id, &[]);
then {
return;
let lhs_constructor = last_path_segment(qpath);
if method_path.ident.name == sym::next
- && match_trait_method(cx, match_expr, &paths::ITERATOR)
+ && is_trait_method(cx, match_expr, sym::Iterator)
&& lhs_constructor.ident.name == sym::Some
&& (pat_args.is_empty()
|| !is_refutable(cx, &pat_args[0])
-use crate::utils::{in_macro, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::snippet;
use hir::def::{DefKind, Res};
use if_chain::if_chain;
use rustc_ast::ast;
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet;
+use clippy_utils::{is_entrypoint_fn, is_no_std_crate};
+use if_chain::if_chain;
use rustc_hir::{Crate, Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use crate::utils::{is_entrypoint_fn, is_no_std_crate, snippet, span_lint_and_help};
-use if_chain::if_chain;
-
declare_clippy_lint! {
/// **What it does:** Checks for recursion using the entrypoint.
///
-use crate::utils::paths::FUTURE_FROM_GENERATOR;
-use crate::utils::{match_function_call, position_before_rarrow, snippet_block, snippet_opt, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::match_function_call;
+use clippy_utils::paths::FUTURE_FROM_GENERATOR;
+use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
-use crate::{
- map_unit_fn::OPTION_MAP_UNIT_FN,
- matches::MATCH_AS_REF,
- utils::{
- can_partially_move_ty, is_allowed, is_type_diagnostic_item, match_def_path, match_var, paths,
- peel_hir_expr_refs, peel_mid_ty_refs_is_mutable, snippet_with_applicability, snippet_with_context,
- span_lint_and_sugg,
- },
-};
+use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
+use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
+use clippy_utils::{is_allowed, is_else_clause_of_if_let_else, match_def_path, match_var, paths, peel_hir_expr_refs};
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::Applicability;
use rustc_hir::{
def::Res,
intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
- Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, Path, QPath,
+ Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
return;
}
- if let ExprKind::Match(scrutinee, [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], _) =
- expr.kind
+ if let ExprKind::Match(
+ scrutinee,
+ [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }],
+ match_kind,
+ ) = expr.kind
{
let (scrutinee_ty, ty_ref_count, ty_mutability) =
peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
// Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
// it's being passed by value.
let scrutinee = peel_hir_expr_refs(scrutinee).0;
- let scrutinee_str = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
+ let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
let scrutinee_str =
if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
format!("({})", scrutinee_str)
"|{}{}| {}",
annotation,
some_binding,
- snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app)
+ snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
)
},
}
// TODO: handle explicit reference annotations.
format!(
"|{}| {}",
- snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app),
- snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app)
+ snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0,
+ snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
)
} else {
// Refutable bindings and mixed reference annotations can't be handled by `map`.
expr.span,
"manual implementation of `Option::map`",
"try this",
- format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str),
+ if matches!(match_kind, MatchSource::IfLetDesugar { .. }) && is_else_clause_of_if_let_else(cx.tcx, expr)
+ {
+ format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str)
+ } else {
+ format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str)
+ },
app,
);
}
-use crate::utils::{meets_msrv, snippet_opt, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::meets_msrv;
+use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
-use rustc_ast::ast::{Attribute, Item, ItemKind, FieldDef, Variant, VariantData, VisibilityKind};
+use rustc_ast::ast::{Attribute, FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind};
use rustc_attr as attr;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
-use crate::utils::{
- indent_of, is_type_diagnostic_item, match_qpath, path_to_local_id, paths, reindent_multiline, snippet_opt,
- span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{match_qpath, path_to_local_id, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, PatKind};
use crate::consts::{constant, Constant};
-use crate::utils::usage::mutated_variables;
-use crate::utils::{
- eq_expr_value, higher, match_def_path, meets_msrv, multispan_sugg, paths, snippet, span_lint_and_then,
-};
-
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::usage::mutated_variables;
+use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, paths};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::def::Res;
use crate::consts::constant_simple;
-use crate::utils;
-use crate::utils::{path_to_local_id, sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::usage::contains_return_break_continue_macro;
+use clippy_utils::{in_constant, match_qpath, path_to_local_id, paths, sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind};
impl LateLintPass<'_> for ManualUnwrapOr {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- if in_external_macro(cx.sess(), expr.span) {
+ if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
return;
}
lint_manual_unwrap_or(cx, expr);
if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)|
match arm.pat.kind {
PatKind::Path(ref some_qpath) =>
- utils::match_qpath(some_qpath, &utils::paths::OPTION_NONE),
+ match_qpath(some_qpath, &paths::OPTION_NONE),
PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) =>
- utils::match_qpath(err_qpath, &utils::paths::RESULT_ERR),
+ match_qpath(err_qpath, &paths::RESULT_ERR),
_ => false,
}
);
let unwrap_arm = &arms[1 - idx];
if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
- if utils::match_qpath(unwrap_qpath, &utils::paths::OPTION_SOME)
- || utils::match_qpath(unwrap_qpath, &utils::paths::RESULT_OK);
+ if match_qpath(unwrap_qpath, &paths::OPTION_SOME)
+ || match_qpath(unwrap_qpath, &paths::RESULT_OK);
if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
if path_to_local_id(unwrap_arm.body, binding_hir_id);
- if !utils::usage::contains_return_break_continue_macro(or_arm.body);
+ if !contains_return_break_continue_macro(or_arm.body);
then {
Some(or_arm)
} else {
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 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 is_type_diagnostic_item(cx, ty, sym::result_type) {
Some(Case::Result)
} else {
None
};
if let Some(or_arm) = applicable_or_arm(match_arms);
- if let Some(or_body_snippet) = utils::snippet_opt(cx, or_arm.body.span);
- if let Some(indent) = utils::indent_of(cx, expr.span);
+ if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span);
+ if let Some(indent) = indent_of(cx, expr.span);
if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
then {
let reindented_or_body =
- utils::reindent_multiline(or_body_snippet.into(), true, Some(indent));
- utils::span_lint_and_sugg(
+ reindent_multiline(or_body_snippet.into(), true, Some(indent));
+ span_lint_and_sugg(
cx,
MANUAL_UNWRAP_OR, expr.span,
&format!("this pattern reimplements `{}`", case.unwrap_fn_path()),
-use crate::utils::paths;
-use crate::utils::{
- is_copy, is_type_diagnostic_item, match_trait_method, remove_blocks, snippet_with_applicability, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::remove_blocks;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
if args.len() == 2;
if method.ident.name == sym::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) || is_trait_method(cx, e, sym::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);
-use crate::utils::span_lint_and_help;
-
+use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{
- is_adjusted, is_type_diagnostic_item, match_path, match_trait_method, match_var, paths, remove_blocks,
- span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
if args.len() == 2 && method.ident.name == sym::map;
let caller_ty = cx.typeck_results().expr_ty(&args[0]);
- if match_trait_method(cx, expr, &paths::ITERATOR)
+ if is_trait_method(cx, expr, sym::Iterator)
|| is_type_diagnostic_item(cx, caller_ty, sym::result_type)
|| is_type_diagnostic_item(cx, caller_ty, sym::option_type);
then {
-use crate::utils::{is_type_diagnostic_item, iter_input_pats, method_chain_args, snippet, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{iter_input_pats, method_chain_args};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, MatchSource};
use crate::consts::{constant, miri_to_const, Constant};
-use crate::utils::sugg::Sugg;
-use crate::utils::visitors::LocalUsedVisitor;
-use crate::utils::{
- expr_block, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
- is_type_diagnostic_item, is_wild, match_qpath, match_type, meets_msrv, multispan_sugg, path_to_local,
- path_to_local_id, peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block,
- snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
- span_lint_and_then, strip_pat_refs,
+use clippy_utils::diagnostics::{
+ multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
};
-use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
+use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
+use clippy_utils::visitors::LocalUsedVisitor;
+use clippy_utils::{
+ get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local,
+ path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs,
+};
+use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
-use rustc_hir::def::CtorKind;
+use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::{
- Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, Mutability, Node, Pat,
- PatKind, QPath, RangeEnd,
+ self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
+ Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty, TyS};
+use rustc_middle::ty::{self, Ty, TyS, VariantDef};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{Span, Spanned};
}
}
-fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
- let ty = cx.typeck_results().expr_ty(ex);
- if !ty.is_enum() {
- // If there isn't a nice closed set of possible values that can be conveniently enumerated,
- // don't complain about not enumerating the mall.
- return;
+enum CommonPrefixSearcher<'a> {
+ None,
+ Path(&'a [PathSegment<'a>]),
+ Mixed,
+}
+impl CommonPrefixSearcher<'a> {
+ fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
+ match path {
+ [path @ .., _] => self.with_prefix(path),
+ [] => (),
+ }
}
+ fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) {
+ match self {
+ Self::None => *self = Self::Path(path),
+ Self::Path(self_path)
+ if path
+ .iter()
+ .map(|p| p.ident.name)
+ .eq(self_path.iter().map(|p| p.ident.name)) => {},
+ Self::Path(_) => *self = Self::Mixed,
+ Self::Mixed => (),
+ }
+ }
+}
+
+#[allow(clippy::too_many_lines)]
+fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
+ let ty = cx.typeck_results().expr_ty(ex).peel_refs();
+ let adt_def = match ty.kind() {
+ ty::Adt(adt_def, _)
+ if adt_def.is_enum()
+ && !(is_type_diagnostic_item(cx, ty, sym::option_type)
+ || is_type_diagnostic_item(cx, ty, sym::result_type)) =>
+ {
+ adt_def
+ },
+ _ => return,
+ };
+
// First pass - check for violation, but don't do much book-keeping because this is hopefully
// the uncommon case, and the book-keeping is slightly expensive.
let mut wildcard_span = None;
let mut wildcard_ident = None;
+ let mut has_non_wild = false;
for arm in arms {
- if let PatKind::Wild = arm.pat.kind {
- wildcard_span = Some(arm.pat.span);
- } else if let PatKind::Binding(_, _, ident, None) = arm.pat.kind {
- wildcard_span = Some(arm.pat.span);
- wildcard_ident = Some(ident);
+ match peel_hir_pat_refs(arm.pat).0.kind {
+ PatKind::Wild => wildcard_span = Some(arm.pat.span),
+ PatKind::Binding(_, _, ident, None) => {
+ wildcard_span = Some(arm.pat.span);
+ wildcard_ident = Some(ident);
+ },
+ _ => has_non_wild = true,
}
}
+ let wildcard_span = match wildcard_span {
+ Some(x) if has_non_wild => x,
+ _ => return,
+ };
- if let Some(wildcard_span) = wildcard_span {
- // Accumulate the variants which should be put in place of the wildcard because they're not
- // already covered.
+ // Accumulate the variants which should be put in place of the wildcard because they're not
+ // already covered.
+ let mut missing_variants: Vec<_> = adt_def.variants.iter().collect();
- let mut missing_variants = vec![];
- if let ty::Adt(def, _) = ty.kind() {
- for variant in &def.variants {
- missing_variants.push(variant);
+ let mut path_prefix = CommonPrefixSearcher::None;
+ for arm in arms {
+ // Guards mean that this case probably isn't exhaustively covered. Technically
+ // this is incorrect, as we should really check whether each variant is exhaustively
+ // covered by the set of guards that cover it, but that's really hard to do.
+ recurse_or_patterns(arm.pat, |pat| {
+ let path = match &peel_hir_pat_refs(pat).0.kind {
+ PatKind::Path(path) => {
+ #[allow(clippy::match_same_arms)]
+ let id = match cx.qpath_res(path, pat.hir_id) {
+ Res::Def(DefKind::Const | DefKind::ConstParam | DefKind::AnonConst, _) => return,
+ Res::Def(_, id) => id,
+ _ => return,
+ };
+ if arm.guard.is_none() {
+ missing_variants.retain(|e| e.ctor_def_id != Some(id));
+ }
+ path
+ },
+ PatKind::TupleStruct(path, patterns, ..) => {
+ if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
+ let id = cx.qpath_res(path, pat.hir_id).def_id();
+ missing_variants.retain(|e| e.ctor_def_id != Some(id));
+ }
+ path
+ },
+ PatKind::Struct(path, patterns, ..) => {
+ if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
+ let id = cx.qpath_res(path, pat.hir_id).def_id();
+ missing_variants.retain(|e| e.def_id != id);
+ }
+ path
+ },
+ _ => return,
+ };
+ match path {
+ QPath::Resolved(_, path) => path_prefix.with_path(path.segments),
+ QPath::TypeRelative(
+ hir::Ty {
+ kind: TyKind::Path(QPath::Resolved(_, path)),
+ ..
+ },
+ _,
+ ) => path_prefix.with_prefix(path.segments),
+ _ => (),
}
- }
+ });
+ }
- for arm in arms {
- if arm.guard.is_some() {
- // Guards mean that this case probably isn't exhaustively covered. Technically
- // this is incorrect, as we should really check whether each variant is exhaustively
- // covered by the set of guards that cover it, but that's really hard to do.
- continue;
- }
- if let PatKind::Path(ref path) = arm.pat.kind {
- if let QPath::Resolved(_, p) = path {
- missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
- }
- } else if let PatKind::TupleStruct(QPath::Resolved(_, p), ref patterns, ..) = arm.pat.kind {
- // Some simple checks for exhaustive patterns.
- // There is a room for improvements to detect more cases,
- // but it can be more expensive to do so.
- let is_pattern_exhaustive =
- |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None));
- if patterns.iter().all(is_pattern_exhaustive) {
- missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
+ let format_suggestion = |variant: &VariantDef| {
+ format!(
+ "{}{}{}{}",
+ if let Some(ident) = wildcard_ident {
+ format!("{} @ ", ident.name)
+ } else {
+ String::new()
+ },
+ if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
+ let mut s = String::new();
+ for seg in path_prefix {
+ s.push_str(&seg.ident.as_str());
+ s.push_str("::");
}
+ s
+ } else {
+ let mut s = cx.tcx.def_path_str(adt_def.did);
+ s.push_str("::");
+ s
+ },
+ variant.ident.name,
+ match variant.ctor_kind {
+ CtorKind::Fn if variant.fields.len() == 1 => "(_)",
+ CtorKind::Fn => "(..)",
+ CtorKind::Const => "",
+ CtorKind::Fictive => "{ .. }",
}
- }
-
- let mut suggestion: Vec<String> = missing_variants
- .iter()
- .map(|v| {
- let suffix = match v.ctor_kind {
- CtorKind::Fn => "(..)",
- CtorKind::Const | CtorKind::Fictive => "",
- };
- let ident_str = if let Some(ident) = wildcard_ident {
- format!("{} @ ", ident.name)
- } else {
- String::new()
- };
- // This path assumes that the enum type is imported into scope.
- format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix)
- })
- .collect();
-
- if suggestion.is_empty() {
- return;
- }
-
- let mut message = "wildcard match will miss any future added variants";
+ )
+ };
- if let ty::Adt(def, _) = ty.kind() {
- if def.is_variant_list_non_exhaustive() {
- message = "match on non-exhaustive enum doesn't explicitly match all known variants";
- suggestion.push(String::from("_"));
- }
- }
+ match missing_variants.as_slice() {
+ [] => (),
+ [x] if !adt_def.is_variant_list_non_exhaustive() => span_lint_and_sugg(
+ cx,
+ MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+ wildcard_span,
+ "wildcard matches only a single variant and will also match any future added variants",
+ "try this",
+ format_suggestion(x),
+ Applicability::MaybeIncorrect,
+ ),
+ variants => {
+ let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect();
+ let message = if adt_def.is_variant_list_non_exhaustive() {
+ suggestions.push("_".into());
+ "wildcard matches known variants and will also match future added variants"
+ } else {
+ "wildcard match will also match any future added variants"
+ };
- if suggestion.len() == 1 {
- // No need to check for non-exhaustive enum as in that case len would be greater than 1
span_lint_and_sugg(
cx,
- MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+ WILDCARD_ENUM_MATCH_ARM,
wildcard_span,
message,
"try this",
- suggestion[0].clone(),
+ suggestions.join(" | "),
Applicability::MaybeIncorrect,
)
- };
-
- span_lint_and_sugg(
- cx,
- WILDCARD_ENUM_MATCH_ARM,
- wildcard_span,
- message,
- "try this",
- suggestion.join(" | "),
- Applicability::MaybeIncorrect,
- )
- }
+ },
+ };
}
// If the block contains only a `panic!` macro (as expression or statement)
}
}
+#[allow(clippy::too_many_lines)]
fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
return;
indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
cbrace_start = format!("{{\n{}", indent);
}
- };
+ }
+ // If the parent is already an arm, and the body is another match statement,
+ // we need curly braces around suggestion
+ let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id);
+ if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) {
+ if let ExprKind::Match(..) = arm.body.kind {
+ cbrace_end = format!("\n{}}}", indent);
+ // Fix body indent due to the match
+ indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
+ cbrace_start = format!("{{\n{}", indent);
+ }
+ }
(
expr.span,
format!(
mod redundant_pattern_match {
use super::REDUNDANT_PATTERN_MATCHING;
- use crate::utils::{match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
+ use clippy_utils::diagnostics::span_lint_and_then;
+ use clippy_utils::source::snippet;
+ use clippy_utils::{is_trait_method, match_qpath, paths};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
if keyword == "while";
if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
if method_path.ident.name == sym::next;
- if match_trait_method(cx, op, &paths::ITERATOR);
+ if is_trait_method(cx, op, sym::Iterator);
then {
return;
}
-use crate::utils::{match_def_path, paths, snippet, span_lint_and_then, walk_ptrs_ty_depth};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::walk_ptrs_ty_depth;
+use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
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 std::iter;
declare_clippy_lint! {
-use crate::utils::{match_def_path, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_def_path, paths};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{
- in_macro, match_def_path, match_qpath, meets_msrv, paths, snippet, snippet_with_applicability, span_lint_and_help,
- span_lint_and_sugg, span_lint_and_then,
-};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::is_diagnostic_assoc_item;
+use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::{in_macro, match_def_path, match_qpath, meets_msrv, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
+use rustc_hir::def_id::DefId;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
}
}
+/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
+/// constructor from the std library
+fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
+ let std_types_symbols = &[
+ sym::string_type,
+ sym::vec_type,
+ sym::vecdeque_type,
+ sym::LinkedList,
+ sym::hashmap_type,
+ sym::BTreeMap,
+ sym::hashset_type,
+ sym::BTreeSet,
+ sym::BinaryHeap,
+ ];
+
+ if std_types_symbols
+ .iter()
+ .any(|symbol| is_diagnostic_assoc_item(cx, def_id, *symbol))
+ {
+ if let QPath::TypeRelative(_, ref method) = path {
+ if method.ident.name == sym::new {
+ return true;
+ }
+ }
+ }
+
+ false
+}
+
fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
if let ExprKind::Call(ref repl_func, _) = src.kind {
if_chain! {
if !in_external_macro(cx.tcx.sess, expr_span);
if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
- if match_def_path(cx, repl_def_id, &paths::DEFAULT_TRAIT_METHOD);
+ if is_diagnostic_assoc_item(cx, repl_def_id, sym::Default)
+ || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
+
then {
span_lint_and_then(
cx,
use super::{contains_return, BIND_INSTEAD_OF_MAP};
-use crate::utils::{
- in_macro, match_qpath, match_type, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet,
- snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, visitors::find_all_ret_expressions,
-};
+use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::{snippet, snippet_with_macro_callsite};
+use clippy_utils::ty::match_type;
+use clippy_utils::{in_macro, match_qpath, method_calls, paths, remove_blocks, visitors::find_all_ret_expressions};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use crate::utils::{is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{method_chain_args, single_segment_path};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_lint::Lint;
+use rustc_middle::ty;
+use rustc_span::sym;
+
+/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ info: &crate::methods::BinaryExprInfo<'_>,
+ chain_methods: &[&str],
+ lint: &'static Lint,
+ suggest: &str,
+) -> bool {
+ if_chain! {
+ if let Some(args) = method_chain_args(info.chain, chain_methods);
+ if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind;
+ 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;
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
+
+ if *self_ty.kind() != ty::Str {
+ return false;
+ }
+
+ span_lint_and_sugg(
+ cx,
+ lint,
+ info.expr.span,
+ &format!("you should use the `{}` method", suggest),
+ "like this",
+ format!("{}{}.{}({})",
+ if info.eq { "" } else { "!" },
+ snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
+ suggest,
+ snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
+ applicability,
+ );
+
+ return true;
+ }
+ }
+
+ false
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::method_chain_args;
+use clippy_utils::source::snippet_with_applicability;
+use if_chain::if_chain;
+use rustc_ast::ast;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_lint::Lint;
+
+/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`.
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ info: &crate::methods::BinaryExprInfo<'_>,
+ chain_methods: &[&str],
+ lint: &'static Lint,
+ suggest: &str,
+) -> bool {
+ if_chain! {
+ if let Some(args) = method_chain_args(info.chain, chain_methods);
+ if let hir::ExprKind::Lit(ref lit) = info.other.kind;
+ if let ast::LitKind::Char(c) = lit.node;
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ lint,
+ info.expr.span,
+ &format!("you should use the `{}` method", suggest),
+ "like this",
+ format!("{}{}.{}('{}')",
+ if info.eq { "" } else { "!" },
+ snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
+ suggest,
+ c),
+ applicability,
+ );
+
+ true
+ } else {
+ false
+ }
+ }
+}
--- /dev/null
+use crate::methods::chars_cmp;
+use rustc_lint::LateContext;
+
+use super::CHARS_LAST_CMP;
+
+/// Checks for the `CHARS_LAST_CMP` lint.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
+ if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") {
+ true
+ } else {
+ chars_cmp::check(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with")
+ }
+}
--- /dev/null
+use crate::methods::chars_cmp_with_unwrap;
+use rustc_lint::LateContext;
+
+use super::CHARS_LAST_CMP;
+
+/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
+ if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") {
+ true
+ } else {
+ chars_cmp_with_unwrap::check(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with")
+ }
+}
--- /dev/null
+use rustc_lint::LateContext;
+
+use super::CHARS_NEXT_CMP;
+
+/// Checks for the `CHARS_NEXT_CMP` lint.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
+ crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with")
+}
--- /dev/null
+use rustc_lint::LateContext;
+
+use super::CHARS_NEXT_CMP;
+
+/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
+ crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with")
+}
-use crate::utils::{is_copy, span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
+use clippy_utils::ty::is_copy;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty;
+use rustc_span::symbol::{sym, Symbol};
use std::iter;
use super::CLONE_DOUBLE_REF;
use super::CLONE_ON_COPY;
/// Checks for the `CLONE_ON_COPY` lint.
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
+ if !(args.len() == 1 && method_name == sym::clone) {
+ return;
+ }
+ let arg = &args[0];
+ let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
let ty = cx.typeck_results().expr_ty(expr);
if let ty::Ref(_, inner, _) = arg_ty.kind() {
if let ty::Ref(_, innermost, _) = inner.kind() {
-use crate::utils::{is_type_diagnostic_item, match_type, paths, snippet_with_macro_callsite, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::paths;
+use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_span::symbol::sym;
+use rustc_span::symbol::{sym, Symbol};
use super::CLONE_ON_REF_PTR;
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
+ if !(args.len() == 1 && method_name == sym::clone) {
+ return;
+ }
+ let arg = &args[0];
let obj_ty = cx.typeck_results().expr_ty(arg).peel_refs();
if let ty::Adt(_, subst) = obj_ty.kind() {
-use crate::utils::{is_expn_of, is_type_diagnostic_item, snippet, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_expn_of;
+use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use crate::utils::{is_type_diagnostic_item, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::sym;
-use crate::utils::{get_parent_expr, match_type, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::match_type;
+use clippy_utils::{get_parent_expr, paths};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use crate::utils::{match_trait_method, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_trait_method;
use rustc_hir as hir;
use rustc_lint::LateContext;
+use rustc_span::sym;
use super::FILTER_MAP;
/// lint use of `filter().flat_map()` for `Iterators`
-pub(super) fn check<'tcx>(
- cx: &LateContext<'tcx>,
- expr: &'tcx hir::Expr<'_>,
- _filter_args: &'tcx [hir::Expr<'_>],
- _map_args: &'tcx [hir::Expr<'_>],
-) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
// lint if caller of `.filter().flat_map()` is an Iterator
- if match_trait_method(cx, expr, &paths::ITERATOR) {
+ if is_trait_method(cx, expr, sym::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()`";
-use crate::utils::{match_trait_method, path_to_local_id, paths, snippet, span_lint_and_sugg, SpanlessEq};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::{is_trait_method, path_to_local_id, SpanlessEq};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
if_chain! {
if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind;
if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind;
- if match_trait_method(cx, map_recv, &paths::ITERATOR);
+ if is_trait_method(cx, map_recv, sym::Iterator);
// filter(|x| ...is_some())...
if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind;
-use crate::utils::{match_trait_method, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_trait_method;
use rustc_hir as hir;
use rustc_lint::LateContext;
+use rustc_span::sym;
use super::FILTER_MAP;
/// lint use of `filter_map().flat_map()` for `Iterators`
-pub(super) fn check<'tcx>(
- cx: &LateContext<'tcx>,
- expr: &'tcx hir::Expr<'_>,
- _filter_args: &'tcx [hir::Expr<'_>],
- _map_args: &'tcx [hir::Expr<'_>],
-) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
// lint if caller of `.filter_map().flat_map()` is an Iterator
- if match_trait_method(cx, expr, &paths::ITERATOR) {
+ if is_trait_method(cx, expr, sym::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()`";
-use crate::utils::{match_qpath, match_trait_method, path_to_local_id, paths, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::{source_map::Span, sym};
use super::FILTER_MAP_IDENTITY;
filter_map_args: &[hir::Expr<'_>],
filter_map_span: Span,
) {
- if match_trait_method(cx, expr, &paths::ITERATOR) {
+ if is_trait_method(cx, expr, sym::Iterator) {
let arg_node = &filter_map_args[1].kind;
let apply_lint = |message: &str| {
-use crate::utils::{match_trait_method, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_trait_method;
use rustc_hir as hir;
use rustc_lint::LateContext;
+use rustc_span::sym;
use super::FILTER_MAP;
/// lint use of `filter_map().map()` for `Iterators`
-pub(super) fn check<'tcx>(
- cx: &LateContext<'tcx>,
- expr: &'tcx hir::Expr<'_>,
- _filter_args: &'tcx [hir::Expr<'_>],
- _map_args: &'tcx [hir::Expr<'_>],
-) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
// lint if caller of `.filter_map().map()` is an Iterator
- if match_trait_method(cx, expr, &paths::ITERATOR) {
+ if is_trait_method(cx, expr, sym::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);
-use crate::utils::{match_trait_method, meets_msrv, paths, snippet, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::source::snippet;
+use clippy_utils::{is_trait_method, meets_msrv};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_semver::RustcVersion;
+use rustc_span::sym;
use super::FILTER_MAP_NEXT;
filter_args: &'tcx [hir::Expr<'_>],
msrv: Option<&RustcVersion>,
) {
- if match_trait_method(cx, expr, &paths::ITERATOR) {
+ if is_trait_method(cx, expr, sym::Iterator) {
if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) {
return;
}
-use crate::utils::{match_trait_method, paths, snippet, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
+use rustc_span::sym;
use super::FILTER_NEXT;
/// lint use of `filter().next()` for `Iterators`
pub(super) fn check<'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) {
+ if is_trait_method(cx, expr, sym::Iterator) {
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, "..");
-use crate::utils::{match_qpath, match_trait_method, paths, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{is_trait_method, match_qpath, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::{source_map::Span, sym};
use super::FLAT_MAP_IDENTITY;
flat_map_args: &'tcx [hir::Expr<'_>],
flat_map_span: Span,
) {
- if match_trait_method(cx, expr, &paths::ITERATOR) {
+ if is_trait_method(cx, expr, sym::Iterator) {
let arg_node = &flat_map_args[1].kind;
let apply_lint = |message: &str| {
-use crate::utils::{get_trait_def_id, implements_trait, paths, span_lint_and_sugg, sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{get_trait_def_id, match_qpath, paths, sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
+use rustc_hir::ExprKind;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::Ty;
+use rustc_span::sym;
use super::FROM_ITER_INSTEAD_OF_COLLECT;
-pub(super) fn check(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]);
-
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func_kind: &ExprKind<'_>) {
if_chain! {
+ if let hir::ExprKind::Path(path) = func_kind;
+ if match_qpath(path, &["from_iter"]);
+ let ty = cx.typeck_results().expr_ty(expr);
+ let arg_ty = cx.typeck_results().expr_ty(&args[0]);
if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR);
- if let Some(iter_id) = get_trait_def_id(cx, &paths::ITERATOR);
+ if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]);
then {
-use crate::methods::derefs_to_slice;
-use crate::utils::{
- get_parent_expr, is_type_diagnostic_item, match_type, paths, snippet_with_applicability, span_lint_and_sugg,
-};
+use super::utils::derefs_to_slice;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
+use clippy_utils::{get_parent_expr, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use crate::utils::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use super::INEFFICIENT_TO_STRING;
-use crate::utils::{
- is_type_diagnostic_item, match_def_path, paths, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty_depth,
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth};
+use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
-use rustc_span::sym;
+use rustc_span::symbol::{sym, Symbol};
+
+use super::INEFFICIENT_TO_STRING;
/// Checks for the `INEFFICIENT_TO_STRING` lint
-pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) {
+pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
if_chain! {
+ if args.len() == 1 && method_name == sym!(to_string);
if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD);
if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id);
+ let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
let self_ty = substs.type_at(0);
let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty);
if deref_count >= 1;
self_ty, deref_self_ty
));
let mut applicability = Applicability::MachineApplicable;
- let arg_snippet = snippet_with_applicability(cx, arg.span, "..", &mut applicability);
+ let arg_snippet = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
diag.span_suggestion(
expr.span,
"try dereferencing the receiver",
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_trait_method;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
-
-use crate::utils::{match_trait_method, paths, span_lint_and_help};
+use rustc_span::{source_map::Span, sym};
use super::INSPECT_FOR_EACH;
/// lint use of `inspect().for_each()` for `Iterators`
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) {
- if match_trait_method(cx, expr, &paths::ITERATOR) {
+ if is_trait_method(cx, expr, sym::Iterator) {
let msg = "called `inspect(..).for_each(..)` on an `Iterator`";
let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`";
span_lint_and_help(
-use crate::utils::{has_iter_method, match_trait_method, paths, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::ty::has_iter_method;
+use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use rustc_span::source_map::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{sym, Symbol};
use super::INTO_ITER_ON_REF;
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_>, method_span: Span) {
- if !match_trait_method(cx, expr, &paths::INTO_ITERATOR) {
- return;
- }
- if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ref_ty) {
- span_lint_and_sugg(
- cx,
- INTO_ITER_ON_REF,
- method_span,
- &format!(
- "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`",
- method_name, kind,
- ),
- "call directly",
- method_name.to_string(),
- Applicability::MachineApplicable,
- );
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ expr: &hir::Expr<'_>,
+ method_span: Span,
+ method_name: Symbol,
+ args: &[hir::Expr<'_>],
+) {
+ let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
+ if_chain! {
+ if let ty::Ref(..) = self_ty.kind();
+ if method_name == sym::into_iter;
+ if is_trait_method(cx, expr, sym::IntoIterator);
+ if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty);
+ then {
+ span_lint_and_sugg(
+ cx,
+ INTO_ITER_ON_REF,
+ method_span,
+ &format!(
+ "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`",
+ method_name, kind,
+ ),
+ "call directly",
+ method_name.to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
}
}
-use crate::methods::derefs_to_slice;
-use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg};
+use crate::methods::utils::derefs_to_slice;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use crate::methods::derefs_to_slice;
-use crate::utils::{is_type_diagnostic_item, match_type, paths, snippet_with_applicability, span_lint_and_sugg};
-
+use super::utils::derefs_to_slice;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::paths;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
-use crate::methods::derefs_to_slice;
-use crate::utils::{get_parent_expr, higher, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg};
+use super::utils::derefs_to_slice;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{get_parent_expr, higher};
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_errors::Applicability;
);
}
}
- } 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(_, _)
- )
- {
+ } else if is_vec_or_array(cx, caller_expr) {
// caller is a Vec or an Array
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
);
}
}
+
+fn is_vec_or_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool {
+ is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type)
+ || matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
+}
-use crate::methods::derefs_to_slice;
+use super::utils::derefs_to_slice;
use crate::methods::iter_nth_zero;
-use crate::utils::{is_type_diagnostic_item, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use crate::consts::{constant, Constant};
-use crate::utils::{match_trait_method, paths, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet_with_applicability;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
+use rustc_span::sym;
use super::ITER_NTH_ZERO;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_args: &'tcx [hir::Expr<'_>]) {
if_chain! {
- if match_trait_method(cx, expr, &paths::ITERATOR);
+ if is_trait_method(cx, expr, sym::Iterator);
if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &nth_args[1]);
then {
let mut applicability = Applicability::MachineApplicable;
-use crate::utils::{match_trait_method, paths, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
+use rustc_span::sym;
use super::ITER_SKIP_NEXT;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir::Expr<'_>]) {
// lint if caller of skip is an Iterator
- if match_trait_method(cx, expr, &paths::ITERATOR) {
+ if is_trait_method(cx, expr, sym::Iterator) {
if let [caller, n] = skip_args {
- let hint = format!(".nth({})", snippet(cx, n.span, ".."));
span_lint_and_sugg(
cx,
ITER_SKIP_NEXT,
expr.span.trim_start(caller.span).unwrap(),
"called `skip(..).next()` on an iterator",
"use `nth` instead",
- hint,
+ format!(".nth({})", snippet(cx, n.span, "..")),
Applicability::MachineApplicable,
);
}
use crate::consts::{constant, Constant};
-use crate::utils::{match_trait_method, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_trait_method;
use rustc_hir as hir;
use rustc_lint::LateContext;
+use rustc_span::sym;
use super::ITERATOR_STEP_BY_ZERO;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) {
- if match_trait_method(cx, expr, &paths::ITERATOR) {
+ if is_trait_method(cx, expr, sym::Iterator) {
if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &args[1]) {
span_lint(
cx,
-use crate::utils::{match_qpath, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::match_qpath;
+use clippy_utils::source::snippet_with_applicability;
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_errors::Applicability;
-use crate::utils::{is_type_diagnostic_item, match_trait_method, paths, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
if_chain! {
// called on Iterator
if let [map_expr] = collect_args;
- if match_trait_method(cx, map_expr, &paths::ITERATOR);
+ if is_trait_method(cx, map_expr, sym::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);
-use crate::utils::{is_type_diagnostic_item, match_trait_method, paths, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
/// lint use of `map().flatten()` for `Iterators` and 'Options'
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) {
// lint if caller of `.map().flatten()` is an Iterator
- if match_trait_method(cx, expr, &paths::ITERATOR) {
+ if is_trait_method(cx, expr, sym::Iterator) {
let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]);
let is_map_to_option = match map_closure_ty.kind() {
ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
-use crate::utils::usage::mutated_variables;
-use crate::utils::{is_type_diagnostic_item, meets_msrv, snippet, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::meets_msrv;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::usage::mutated_variables;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
mod bind_instead_of_map;
mod bytes_nth;
+mod chars_cmp;
+mod chars_cmp_with_unwrap;
+mod chars_last_cmp;
+mod chars_last_cmp_with_unwrap;
+mod chars_next_cmp;
+mod chars_next_cmp_with_unwrap;
mod clone_on_copy;
mod clone_on_ref_ptr;
mod expect_fun_call;
mod option_map_unwrap_or;
mod or_fun_call;
mod search_is_some;
+mod single_char_add_str;
mod single_char_insert_string;
mod single_char_pattern;
mod single_char_push_string;
mod unnecessary_lazy_eval;
mod unwrap_used;
mod useless_asref;
+mod utils;
mod wrong_self_convention;
mod zst_offset;
use bind_instead_of_map::BindInsteadOfMap;
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
+use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, method_calls, paths, return_ty};
use if_chain::if_chain;
-use rustc_ast::ast;
-use rustc_errors::Applicability;
use rustc_hir as hir;
-use rustc_hir::{TraitItem, TraitItemKind};
-use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
+use rustc_hir::def::Res;
+use rustc_hir::{PrimTy, QPath, TraitItem, TraitItemKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
use rustc_semver::RustcVersion;
use rustc_span::symbol::{sym, SymbolStr};
use rustc_typeck::hir_ty_to_ty;
-use crate::utils::{
- contains_return, contains_ty, get_trait_def_id, implements_trait, in_macro, is_copy, is_type_diagnostic_item,
- iter_input_pats, match_def_path, match_qpath, method_calls, method_chain_args, paths, return_ty,
- single_segment_path, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
-};
-
declare_clippy_lint! {
/// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.
///
/// **What it does:** Checks for methods with certain name prefixes and which
/// doesn't match how self is taken. The actual rules are:
///
- /// |Prefix |`self` taken |
- /// |-------|----------------------|
- /// |`as_` |`&self` or `&mut self`|
- /// |`from_`| none |
- /// |`into_`|`self` |
- /// |`is_` |`&self` or none |
- /// |`to_` |`&self` |
+ /// |Prefix |Postfix |`self` taken | `self` type |
+ /// |-------|------------|-----------------------|--------------|
+ /// |`as_` | none |`&self` or `&mut self` | any |
+ /// |`from_`| none | none | any |
+ /// |`into_`| none |`self` | any |
+ /// |`is_` | none |`&self` or none | any |
+ /// |`to_` | `_mut` |`&mut self` | any |
+ /// |`to_` | not `_mut` |`self` | `Copy` |
+ /// |`to_` | not `_mut` |`&self` | not `Copy` |
+ ///
+ /// Please find more info here:
+ /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
///
/// **Why is this bad?** Consistency breeds readability. If you follow the
/// conventions, your users won't be surprised that they, e.g., need to supply a
declare_clippy_lint! {
/// **What it does:** Checks for an iterator or string search (such as `find()`,
- /// `position()`, or `rposition()`) followed by a call to `is_some()`.
+ /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`.
///
- /// **Why is this bad?** Readability, this can be written more concisely as
- /// `_.any(_)` or `_.contains(_)`.
+ /// **Why is this bad?** Readability, this can be written more concisely as:
+ /// * `_.any(_)`, or `_.contains(_)` for `is_some()`,
+ /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
- /// # let vec = vec![1];
+ /// let vec = vec![1];
/// vec.iter().find(|x| **x == 0).is_some();
+ ///
+ /// let _ = "hello world".find("world").is_none();
/// ```
/// Could be written as
/// ```rust
- /// # let vec = vec![1];
+ /// let vec = vec![1];
/// vec.iter().any(|x| *x == 0);
+ ///
+ /// let _ = !"hello world".contains("world");
/// ```
pub SEARCH_IS_SOME,
complexity,
- "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains()`"
+ "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
}
declare_clippy_lint! {
["next", "skip_while"] => skip_while_next::check(cx, expr, arg_lists[1]),
["next", "iter"] => iter_next_slice::check(cx, expr, arg_lists[1]),
["map", "filter"] => filter_map::check(cx, expr, false),
- ["map", "filter_map"] => filter_map_map::check(cx, expr, arg_lists[1], arg_lists[0]),
+ ["map", "filter_map"] => filter_map_map::check(cx, expr),
["next", "filter_map"] => filter_map_next::check(cx, expr, arg_lists[1], self.msrv.as_ref()),
["map", "find"] => filter_map::check(cx, expr, true),
- ["flat_map", "filter"] => filter_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]),
- ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]),
+ ["flat_map", "filter"] => filter_flat_map::check(cx, expr),
+ ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr),
["flat_map", ..] => flat_map_identity::check(cx, expr, arg_lists[0], method_spans[0]),
["flatten", "map"] => map_flatten::check(cx, expr, arg_lists[1]),
- ["is_some", "find"] => search_is_some::check(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]),
- ["is_some", "position"] => {
- search_is_some::check(cx, expr, "position", arg_lists[1], arg_lists[0], method_spans[1])
+ [option_check_method, "find"] if "is_some" == *option_check_method || "is_none" == *option_check_method => {
+ search_is_some::check(
+ cx,
+ expr,
+ "find",
+ option_check_method,
+ arg_lists[1],
+ arg_lists[0],
+ method_spans[1],
+ )
},
- ["is_some", "rposition"] => {
- search_is_some::check(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1])
+ [option_check_method, "position"]
+ if "is_some" == *option_check_method || "is_none" == *option_check_method =>
+ {
+ search_is_some::check(
+ cx,
+ expr,
+ "position",
+ option_check_method,
+ arg_lists[1],
+ arg_lists[0],
+ method_spans[1],
+ )
+ },
+ [option_check_method, "rposition"]
+ if "is_some" == *option_check_method || "is_none" == *option_check_method =>
+ {
+ search_is_some::check(
+ cx,
+ expr,
+ "rposition",
+ option_check_method,
+ arg_lists[1],
+ arg_lists[0],
+ method_spans[1],
+ )
},
["extend", ..] => string_extend_chars::check(cx, expr, arg_lists[0]),
["count", "into_iter"] => iter_count::check(cx, expr, &arg_lists[1], "into_iter"),
unnecessary_filter_map::check(cx, expr, arg_lists[0]);
filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]);
},
- ["count", "map"] => suspicious_map::check(cx, expr),
+ ["count", "map"] => suspicious_map::check(cx, expr, arg_lists[1], arg_lists[0]),
["assume_init"] => uninit_assumed_init::check(cx, &arg_lists[0][0], expr),
["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => {
manual_saturating_arithmetic::check(cx, expr, &arg_lists, &arith["checked_".len()..])
match expr.kind {
hir::ExprKind::Call(ref func, ref args) => {
- if let hir::ExprKind::Path(path) = &func.kind {
- if match_qpath(path, &["from_iter"]) {
- from_iter_instead_of_collect::check(cx, expr, args);
- }
- }
+ from_iter_instead_of_collect::check(cx, expr, args, &func.kind);
},
hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => {
or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
-
- let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
- if args.len() == 1 && method_call.ident.name == sym::clone {
- clone_on_copy::check(cx, expr, &args[0], self_ty);
- clone_on_ref_ptr::check(cx, expr, &args[0]);
- }
- if args.len() == 1 && method_call.ident.name == sym!(to_string) {
- inefficient_to_string::check(cx, expr, &args[0], self_ty);
- }
-
- 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) {
- single_char_push_string::check(cx, expr, args);
- } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) {
- single_char_insert_string::check(cx, expr, args);
- }
- }
-
- match self_ty.kind() {
- ty::Ref(_, ty, _) if *ty.kind() == ty::Str => {
- for &(method, pos) in &PATTERN_METHODS {
- if method_call.ident.name.as_str() == method && args.len() > pos {
- single_char_pattern::check(cx, expr, &args[pos]);
- }
- }
- },
- ty::Ref(..) if method_call.ident.name == sym::into_iter => {
- into_iter_on_ref::check(cx, expr, self_ty, *method_span);
- },
- _ => (),
- }
+ clone_on_copy::check(cx, expr, method_call.ident.name, args);
+ clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args);
+ inefficient_to_string::check(cx, expr, method_call.ident.name, args);
+ single_char_add_str::check(cx, expr, args);
+ into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args);
+ single_char_pattern::check(cx, expr, method_call.ident.name, args);
},
hir::ExprKind::Binary(op, ref lhs, ref rhs)
if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne =>
let item = cx.tcx.hir().expect_item(parent);
let self_ty = cx.tcx.type_of(item.def_id);
- // if this impl block implements a trait, lint in trait definition instead
- if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind {
- return;
- }
+ let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
if_chain! {
if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
if let Some(first_arg_ty) = first_arg_ty;
then {
- if cx.access_levels.is_exported(impl_item.hir_id()) {
+ // if this impl block implements a trait, lint in trait definition instead
+ if !implements_trait && cx.access_levels.is_exported(impl_item.hir_id()) {
// check missing trait implementations
for method_config in &TRAIT_METHODS {
if name == method_config.method_name &&
sig.decl.inputs.len() == method_config.param_count &&
- method_config.output_type.matches(cx, &sig.decl.output) &&
+ method_config.output_type.matches(&sig.decl.output) &&
method_config.self_kind.matches(cx, self_ty, first_arg_ty) &&
fn_header_equals(method_config.fn_header, sig.header) &&
method_config.lifetime_param_cond(&impl_item)
item.vis.node.is_pub(),
self_ty,
first_arg_ty,
- first_arg.pat.span
+ first_arg.pat.span,
+ false
);
}
}
+ // if this impl block implements a trait, lint in trait definition instead
+ if implements_trait {
+ return;
+ }
+
if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
let ret_ty = return_ty(cx, impl_item.hir_id());
// walk the return type and check for Self (this does not check associated types)
- if contains_ty(ret_ty, self_ty) {
+ if let Some(self_adt) = self_ty.ty_adt_def() {
+ if contains_adt_constructor(ret_ty, self_adt) {
+ return;
+ }
+ } else if contains_ty(ret_ty, self_ty) {
return;
}
for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
// walk the associated type and check for Self
- if contains_ty(projection_predicate.ty, self_ty) {
+ if let Some(self_adt) = self_ty.ty_adt_def() {
+ if contains_adt_constructor(projection_predicate.ty, self_adt) {
+ return;
+ }
+ } else if contains_ty(projection_predicate.ty, self_ty) {
return;
}
}
false,
self_ty,
first_arg_ty,
- first_arg_span
+ first_arg_span,
+ true
);
}
}
extract_msrv_attr!(LateContext);
}
-
-fn derefs_to_slice<'tcx>(
- cx: &LateContext<'tcx>,
- expr: &'tcx hir::Expr<'tcx>,
- ty: Ty<'tcx>,
-) -> Option<&'tcx hir::Expr<'tcx>> {
- fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
- 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::Array(_, size) => size
- .try_eval_usize(cx.tcx, cx.param_env)
- .map_or(false, |size| size < 32),
- ty::Ref(_, inner, _) => may_slice(cx, inner),
- _ => false,
- }
- }
-
- 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])) {
- Some(&args[0])
- } else {
- None
- }
- } else {
- match ty.kind() {
- ty::Slice(_) => Some(expr),
- ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
- ty::Ref(_, inner, _) => {
- if may_slice(cx, inner) {
- Some(expr)
- } else {
- None
- }
- },
- _ => None,
- }
- }
-}
-
/// Used for `lint_binary_expr_with_method_call`.
#[derive(Copy, Clone)]
struct BinaryExprInfo<'a> {
/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
macro_rules! lint_with_both_lhs_and_rhs {
- ($func:ident, $cx:expr, $info:ident) => {
+ ($func:expr, $cx:expr, $info:ident) => {
if !$func($cx, $info) {
::std::mem::swap(&mut $info.chain, &mut $info.other);
if $func($cx, $info) {
};
}
- lint_with_both_lhs_and_rhs!(lint_chars_next_cmp, cx, info);
- lint_with_both_lhs_and_rhs!(lint_chars_last_cmp, cx, info);
- lint_with_both_lhs_and_rhs!(lint_chars_next_cmp_with_unwrap, cx, info);
- lint_with_both_lhs_and_rhs!(lint_chars_last_cmp_with_unwrap, cx, info);
-}
-
-/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
-fn lint_chars_cmp(
- cx: &LateContext<'_>,
- info: &BinaryExprInfo<'_>,
- chain_methods: &[&str],
- lint: &'static Lint,
- suggest: &str,
-) -> bool {
- if_chain! {
- if let Some(args) = method_chain_args(info.chain, chain_methods);
- if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind;
- 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;
- then {
- let mut applicability = Applicability::MachineApplicable;
- let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
-
- if *self_ty.kind() != ty::Str {
- return false;
- }
-
- span_lint_and_sugg(
- cx,
- lint,
- info.expr.span,
- &format!("you should use the `{}` method", suggest),
- "like this",
- format!("{}{}.{}({})",
- if info.eq { "" } else { "!" },
- snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
- suggest,
- snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
- applicability,
- );
-
- return true;
- }
- }
-
- false
-}
-
-/// Checks for the `CHARS_NEXT_CMP` lint.
-fn lint_chars_next_cmp<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
- lint_chars_cmp(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with")
-}
-
-/// Checks for the `CHARS_LAST_CMP` lint.
-fn lint_chars_last_cmp<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
- if lint_chars_cmp(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") {
- true
- } else {
- lint_chars_cmp(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with")
- }
-}
-
-/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`.
-fn lint_chars_cmp_with_unwrap<'tcx>(
- cx: &LateContext<'tcx>,
- info: &BinaryExprInfo<'_>,
- chain_methods: &[&str],
- lint: &'static Lint,
- suggest: &str,
-) -> bool {
- if_chain! {
- if let Some(args) = method_chain_args(info.chain, chain_methods);
- if let hir::ExprKind::Lit(ref lit) = info.other.kind;
- if let ast::LitKind::Char(c) = lit.node;
- then {
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- lint,
- info.expr.span,
- &format!("you should use the `{}` method", suggest),
- "like this",
- format!("{}{}.{}('{}')",
- if info.eq { "" } else { "!" },
- snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
- suggest,
- c),
- applicability,
- );
-
- true
- } else {
- false
- }
- }
-}
-
-/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`.
-fn lint_chars_next_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
- lint_chars_cmp_with_unwrap(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with")
-}
-
-/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`.
-fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
- if lint_chars_cmp_with_unwrap(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") {
- true
- } else {
- lint_chars_cmp_with_unwrap(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with")
- }
-}
-
-fn get_hint_if_single_char_arg(
- cx: &LateContext<'_>,
- arg: &hir::Expr<'_>,
- applicability: &mut Applicability,
-) -> Option<String> {
- if_chain! {
- if let hir::ExprKind::Lit(lit) = &arg.kind;
- if let ast::LitKind::Str(r, style) = lit.node;
- let string = r.as_str();
- 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 {
- let nhash = nhash as usize;
- // for raw string: r##"a"##
- &snip[(nhash + 2)..(snip.len() - 1 - nhash)]
- } else {
- // for regular string: "a"
- &snip[1..(snip.len() - 1)]
- };
- let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
- Some(hint)
- } else {
- None
- }
- }
+ lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
+ lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
+ lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
+ lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
}
const FN_HEADER: hir::FnHeader = hir::FnHeader {
#[must_use]
fn description(self) -> &'static str {
match self {
- Self::Value => "self by value",
- Self::Ref => "self by reference",
- Self::RefMut => "self by mutable reference",
- Self::No => "no self",
+ Self::Value => "`self` by value",
+ Self::Ref => "`self` by reference",
+ Self::RefMut => "`self` by mutable reference",
+ Self::No => "no `self`",
}
}
}
}
impl OutType {
- fn matches(self, cx: &LateContext<'_>, ty: &hir::FnRetTy<'_>) -> bool {
- let is_unit = |ty: &hir::Ty<'_>| SpanlessEq::new(cx).eq_ty_kind(&ty.kind, &hir::TyKind::Tup(&[]));
+ fn matches(self, ty: &hir::FnRetTy<'_>) -> bool {
+ let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[]));
match (self, ty) {
(Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
(Self::Unit, &hir::FnRetTy::Return(ref ty)) if is_unit(ty) => true,
}
fn is_bool(ty: &hir::Ty<'_>) -> bool {
- if let hir::TyKind::Path(ref p) = ty.kind {
- match_qpath(p, &["bool"])
+ if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
+ matches!(path.res, Res::PrimTy(PrimTy::Bool))
} else {
false
}
-use crate::utils::{implements_trait, is_type_diagnostic_item, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use crate::utils::{
- is_type_diagnostic_item, match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks, snippet,
- span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use crate::utils::{is_type_diagnostic_item, match_qpath, paths, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{match_qpath, paths};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use crate::utils::{differing_macro_contexts, snippet_with_applicability, span_lint_and_then};
-use crate::utils::{is_copy, is_type_diagnostic_item};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::differing_macro_contexts;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_copy;
+use clippy_utils::ty::is_type_diagnostic_item;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
-use crate::utils::eager_or_lazy::is_lazyness_candidate;
-use crate::utils::{
- contains_return, get_trait_def_id, implements_trait, is_type_diagnostic_item, last_path_segment, match_type, paths,
- snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::eager_or_lazy::is_lazyness_candidate;
+use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite};
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type};
+use clippy_utils::{contains_return, get_trait_def_id, last_path_segment, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
+use rustc_hir::{BlockCheckMode, UnsafeSource};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::source_map::Span;
let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
match ty.kind() {
- ty::Slice(_) | ty::Array(_, _) => return,
+ ty::Slice(_) | ty::Array(_, _) | ty::Str => return,
_ => (),
}
hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
},
- _ => {},
+ hir::ExprKind::Block(block, _) => {
+ if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules {
+ if let Some(block_expr) = block.expr {
+ if let hir::ExprKind::MethodCall(..) = block_expr.kind {
+ check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
+ }
+ }
+ }
+ },
+ _ => (),
}
}
}
-use crate::utils::{
- is_type_diagnostic_item, match_trait_method, paths, snippet, snippet_with_applicability, span_lint_and_help,
- span_lint_and_sugg, strip_pat_refs,
-};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_trait_method, strip_pat_refs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use super::SEARCH_IS_SOME;
/// lint searching an Iterator followed by `is_some()`
-/// or calling `find()` on a string followed by `is_some()`
+/// or calling `find()` on a string followed by `is_some()` or `is_none()`
+#[allow(clippy::too_many_lines)]
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
search_method: &str,
+ option_check_method: &str,
search_args: &'tcx [hir::Expr<'_>],
is_some_args: &'tcx [hir::Expr<'_>],
method_span: Span,
) {
// lint if caller of search is an Iterator
- if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) {
+ if is_trait_method(cx, &is_some_args[0], sym::Iterator) {
let msg = format!(
- "called `is_some()` after searching an `Iterator` with `{}`",
- search_method
+ "called `{}()` after searching an `Iterator` with `{}`",
+ option_check_method, search_method
);
- let hint = "this is more succinctly expressed by calling `any()`";
let search_snippet = snippet(cx, search_args[1].span, "..");
if search_snippet.lines().count() <= 1 {
// suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()`
}
};
// add note if not multi-line
- span_lint_and_sugg(
- cx,
- SEARCH_IS_SOME,
- method_span.with_hi(expr.span.hi()),
- &msg,
- "use `any()` instead",
- format!(
- "any({})",
- any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
- ),
- Applicability::MachineApplicable,
- );
+ match option_check_method {
+ "is_some" => {
+ span_lint_and_sugg(
+ cx,
+ SEARCH_IS_SOME,
+ method_span.with_hi(expr.span.hi()),
+ &msg,
+ "use `any()` instead",
+ format!(
+ "any({})",
+ any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
+ ),
+ Applicability::MachineApplicable,
+ );
+ },
+ "is_none" => {
+ let iter = snippet(cx, search_args[0].span, "..");
+ span_lint_and_sugg(
+ cx,
+ SEARCH_IS_SOME,
+ expr.span,
+ &msg,
+ "use `!_.any()` instead",
+ format!(
+ "!{}.any({})",
+ iter,
+ any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
+ ),
+ Applicability::MachineApplicable,
+ );
+ },
+ _ => (),
+ }
} else {
- span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, hint);
+ let hint = format!(
+ "this is more succinctly expressed by calling `any()`{}",
+ if option_check_method == "is_none" {
+ " with negation"
+ } else {
+ ""
+ }
+ );
+ span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, &hint);
}
}
// lint if `find()` is called by `String` or `&str`
if is_string_or_str_slice(&search_args[0]);
if is_string_or_str_slice(&search_args[1]);
then {
- let msg = "called `is_some()` after calling `find()` on a string";
- let mut applicability = Applicability::MachineApplicable;
- let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability);
- span_lint_and_sugg(
- cx,
- SEARCH_IS_SOME,
- method_span.with_hi(expr.span.hi()),
- msg,
- "use `contains()` instead",
- format!("contains({})", find_arg),
- applicability,
- );
+ let msg = format!("called `{}()` after calling `find()` on a string", option_check_method);
+ match option_check_method {
+ "is_some" => {
+ let mut applicability = Applicability::MachineApplicable;
+ let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ SEARCH_IS_SOME,
+ method_span.with_hi(expr.span.hi()),
+ &msg,
+ "use `contains()` instead",
+ format!("contains({})", find_arg),
+ applicability,
+ );
+ },
+ "is_none" => {
+ let string = snippet(cx, search_args[0].span, "..");
+ let mut applicability = Applicability::MachineApplicable;
+ let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ SEARCH_IS_SOME,
+ expr.span,
+ &msg,
+ "use `!_.contains()` instead",
+ format!("!{}.contains({})", string, find_arg),
+ applicability,
+ );
+ },
+ _ => (),
+ }
}
}
}
--- /dev/null
+use crate::methods::{single_char_insert_string, single_char_push_string};
+use clippy_utils::{match_def_path, paths};
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
+ 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) {
+ single_char_push_string::check(cx, expr, args);
+ } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) {
+ single_char_insert_string::check(cx, expr, args);
+ }
+ }
+}
-use crate::methods::get_hint_if_single_char_arg;
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
+use super::utils::get_hint_if_single_char_arg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use crate::methods::get_hint_if_single_char_arg;
-use crate::utils::span_lint_and_sugg;
+use super::utils::get_hint_if_single_char_arg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::symbol::Symbol;
use super::SINGLE_CHAR_PATTERN;
/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
-pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
- let mut applicability = Applicability::MachineApplicable;
- if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) {
- span_lint_and_sugg(
- cx,
- SINGLE_CHAR_PATTERN,
- arg.span,
- "single-character string constant used as pattern",
- "try using a `char` instead",
- hint,
- applicability,
- );
+pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
+ for &(method, pos) in &crate::methods::PATTERN_METHODS {
+ if_chain! {
+ if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(&args[0]).kind();
+ if *ty.kind() == ty::Str;
+ if method_name.as_str() == method && args.len() > pos;
+ let arg = &args[pos];
+ let mut applicability = Applicability::MachineApplicable;
+ if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability);
+ then {
+ span_lint_and_sugg(
+ cx,
+ SINGLE_CHAR_PATTERN,
+ arg.span,
+ "single-character string constant used as pattern",
+ "try using a `char` instead",
+ hint,
+ applicability,
+ );
+ }
+ }
}
}
-use crate::methods::get_hint_if_single_char_arg;
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
+use super::utils::get_hint_if_single_char_arg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use crate::utils::{match_trait_method, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_trait_method;
use rustc_hir as hir;
use rustc_lint::LateContext;
+use rustc_span::sym;
use super::SKIP_WHILE_NEXT;
/// lint use of `skip_while().next()` for `Iterators`
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, _skip_while_args: &'tcx [hir::Expr<'_>]) {
// lint if caller of `.skip_while().next()` is an Iterator
- if match_trait_method(cx, expr, &paths::ITERATOR) {
+ if is_trait_method(cx, expr, sym::Iterator) {
span_lint_and_help(
cx,
SKIP_WHILE_NEXT,
-use crate::utils::{is_type_diagnostic_item, method_chain_args, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::method_chain_args;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
pub(super) fn check(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) {
- let arg = &args[1];
- if let Some(arglists) = method_chain_args(arg, &["chars"]) {
- let target = &arglists[0][0];
- 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 {
- return;
- };
+ if !is_type_diagnostic_item(cx, obj_ty, sym::string_type) {
+ return;
+ }
+ let arg = &args[1];
+ if let Some(arglists) = method_chain_args(arg, &["chars"]) {
+ let target = &arglists[0][0];
+ 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 {
+ return;
+ };
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- STRING_EXTEND_CHARS,
- expr.span,
- "calling `.extend(_.chars())`",
- "try this",
- format!(
- "{}.push_str({}{})",
- snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
- ref_str,
- snippet_with_applicability(cx, target.span, "..", &mut applicability)
- ),
- applicability,
- );
- }
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ STRING_EXTEND_CHARS,
+ expr.span,
+ "calling `.extend(_.chars())`",
+ "try this",
+ format!(
+ "{}.push_str({}{})",
+ snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
+ ref_str,
+ snippet_with_applicability(cx, target.span, "..", &mut applicability)
+ ),
+ applicability,
+ );
}
}
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::usage::mutated_variables;
+use clippy_utils::{expr_or_init, is_trait_method};
+use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::LateContext;
+use rustc_span::sym;
use super::SUSPICIOUS_MAP;
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
- span_lint_and_help(
- cx,
- SUSPICIOUS_MAP,
- expr.span,
- "this call to `map()` won't have an effect on the call to `count()`",
- None,
- "make sure you did not confuse `map` with `filter` or `for_each`",
- );
+pub fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &hir::Expr<'_>,
+ map_args: &[hir::Expr<'_>],
+ count_args: &[hir::Expr<'_>],
+) {
+ if_chain! {
+ if let [count_recv] = count_args;
+ if let [_, map_arg] = map_args;
+ if is_trait_method(cx, count_recv, sym::Iterator);
+ let closure = expr_or_init(cx, map_arg);
+ if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(closure.hir_id);
+ let closure_body = cx.tcx.hir().body(body_id);
+ if !cx.typeck_results().expr_ty(&closure_body.value).is_unit();
+ then {
+ if let Some(map_mutated_vars) = mutated_variables(&closure_body.value, cx) {
+ // A variable is used mutably inside of the closure. Suppress the lint.
+ if !map_mutated_vars.is_empty() {
+ return;
+ }
+ }
+ span_lint_and_help(
+ cx,
+ SUSPICIOUS_MAP,
+ expr.span,
+ "this call to `map()` won't have an effect on the call to `count()`",
+ None,
+ "make sure you did not confuse `map` with `filter` or `for_each`",
+ );
+ }
+ }
}
-use crate::utils::{match_def_path, match_qpath, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_def_path, match_qpath, paths};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use crate::utils::usage::mutated_variables;
-use crate::utils::{match_qpath, match_trait_method, path_to_local_id, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::usage::mutated_variables;
+use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
+use if_chain::if_chain;
use rustc_hir as hir;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
-
-use if_chain::if_chain;
+use rustc_span::sym;
use super::UNNECESSARY_FILTER_MAP;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
- if !match_trait_method(cx, expr, &paths::ITERATOR) {
+ if !is_trait_method(cx, expr, sym::Iterator) {
return;
}
-use crate::utils::{
- match_trait_method, path_to_local_id, paths, remove_blocks, snippet_with_applicability, span_lint_and_sugg,
- strip_pat_refs,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, strip_pat_refs};
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::PatKind;
use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::{source_map::Span, sym};
use super::UNNECESSARY_FOLD;
}
// Check that this is a call to Iterator::fold rather than just some function called fold
- if !match_trait_method(cx, expr, &paths::ITERATOR) {
+ if !is_trait_method(cx, expr, sym::Iterator) {
return;
}
-use crate::utils::{eager_or_lazy, usage};
-use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{eager_or_lazy, usage};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use crate::utils::{is_type_diagnostic_item, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::sym;
-use crate::utils::{
- get_parent_expr, match_trait_method, paths, snippet_with_applicability, span_lint_and_sugg, walk_ptrs_ty_depth,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::walk_ptrs_ty_depth;
+use clippy_utils::{get_parent_expr, match_trait_method, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
--- /dev/null
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::ast;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::symbol::sym;
+
+pub(super) fn derefs_to_slice<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ ty: Ty<'tcx>,
+) -> Option<&'tcx hir::Expr<'tcx>> {
+ fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
+ 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::Array(_, size) => size
+ .try_eval_usize(cx.tcx, cx.param_env)
+ .map_or(false, |size| size < 32),
+ ty::Ref(_, inner, _) => may_slice(cx, inner),
+ _ => false,
+ }
+ }
+
+ 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])) {
+ Some(&args[0])
+ } else {
+ None
+ }
+ } else {
+ match ty.kind() {
+ ty::Slice(_) => Some(expr),
+ ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
+ ty::Ref(_, inner, _) => {
+ if may_slice(cx, inner) {
+ Some(expr)
+ } else {
+ None
+ }
+ },
+ _ => None,
+ }
+ }
+}
+
+pub(super) fn get_hint_if_single_char_arg(
+ cx: &LateContext<'_>,
+ arg: &hir::Expr<'_>,
+ applicability: &mut Applicability,
+) -> Option<String> {
+ if_chain! {
+ if let hir::ExprKind::Lit(lit) = &arg.kind;
+ if let ast::LitKind::Str(r, style) = lit.node;
+ let string = r.as_str();
+ 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 {
+ let nhash = nhash as usize;
+ // for raw string: r##"a"##
+ &snip[(nhash + 2)..(snip.len() - 1 - nhash)]
+ } else {
+ // for regular string: "a"
+ &snip[1..(snip.len() - 1)]
+ };
+ let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
+ Some(hint)
+ } else {
+ None
+ }
+ }
+}
use crate::methods::SelfKind;
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_copy;
use rustc_lint::LateContext;
use rustc_middle::ty::TyS;
use rustc_span::source_map::Span;
use super::WRONG_SELF_CONVENTION;
#[rustfmt::skip]
-const CONVENTIONS: [(Convention, &[SelfKind]); 7] = [
- (Convention::Eq("new"), &[SelfKind::No]),
- (Convention::StartsWith("as_"), &[SelfKind::Ref, SelfKind::RefMut]),
- (Convention::StartsWith("from_"), &[SelfKind::No]),
- (Convention::StartsWith("into_"), &[SelfKind::Value]),
- (Convention::StartsWith("is_"), &[SelfKind::Ref, SelfKind::No]),
- (Convention::Eq("to_mut"), &[SelfKind::RefMut]),
- (Convention::StartsWith("to_"), &[SelfKind::Ref]),
+const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [
+ (&[Convention::Eq("new")], &[SelfKind::No]),
+ (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]),
+ (&[Convention::StartsWith("from_")], &[SelfKind::No]),
+ (&[Convention::StartsWith("into_")], &[SelfKind::Value]),
+ (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]),
+ (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]),
+ (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]),
+
+ // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types).
+ // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
+ (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]),
+ (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), Convention::ImplementsTrait(false)], &[SelfKind::Value]),
];
+
enum Convention {
Eq(&'static str),
StartsWith(&'static str),
+ EndsWith(&'static str),
+ NotEndsWith(&'static str),
+ IsSelfTypeCopy(bool),
+ ImplementsTrait(bool),
}
impl Convention {
#[must_use]
- fn check(&self, other: &str) -> bool {
+ fn check<'tcx>(&self, cx: &LateContext<'tcx>, self_ty: &'tcx TyS<'tcx>, other: &str, is_trait_def: bool) -> bool {
match *self {
Self::Eq(this) => this == other,
Self::StartsWith(this) => other.starts_with(this) && this != other,
+ Self::EndsWith(this) => other.ends_with(this) && this != other,
+ Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, is_trait_def),
+ Self::IsSelfTypeCopy(is_true) => is_true == is_copy(cx, self_ty),
+ Self::ImplementsTrait(is_true) => is_true == is_trait_def,
}
}
}
impl fmt::Display for Convention {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self {
- Self::Eq(this) => this.fmt(f),
- Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)),
+ Self::Eq(this) => format!("`{}`", this).fmt(f),
+ Self::StartsWith(this) => format!("`{}*`", this).fmt(f),
+ Self::EndsWith(this) => format!("`*{}`", this).fmt(f),
+ Self::NotEndsWith(this) => format!("`~{}`", this).fmt(f),
+ Self::IsSelfTypeCopy(is_true) => {
+ format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f)
+ },
+ Self::ImplementsTrait(is_true) => {
+ let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") };
+ format!("Method{} implement{} a trait", negation, s_suffix).fmt(f)
+ },
}
}
}
self_ty: &'tcx TyS<'tcx>,
first_arg_ty: &'tcx TyS<'tcx>,
first_arg_span: Span,
+ is_trait_item: bool,
) {
let lint = if is_pub {
WRONG_PUB_SELF_CONVENTION
} else {
WRONG_SELF_CONVENTION
};
- if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) {
+ if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| {
+ convs
+ .iter()
+ .all(|conv| conv.check(cx, self_ty, item_name, is_trait_item))
+ }) {
if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
- span_lint(
+ let suggestion = {
+ if conventions.len() > 1 {
+ // Don't mention `NotEndsWith` when there is also `StartsWith` convention present
+ let cut_ends_with_conv = conventions.iter().any(|conv| matches!(conv, Convention::StartsWith(_)))
+ && conventions
+ .iter()
+ .any(|conv| matches!(conv, Convention::NotEndsWith(_)));
+
+ let s = conventions
+ .iter()
+ .filter_map(|conv| {
+ if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_)))
+ || matches!(conv, Convention::ImplementsTrait(_))
+ {
+ None
+ } else {
+ Some(conv.to_string())
+ }
+ })
+ .collect::<Vec<_>>()
+ .join(" and ");
+
+ format!("methods with the following characteristics: ({})", &s)
+ } else {
+ format!("methods called {}", &conventions[0])
+ }
+ };
+
+ span_lint_and_help(
cx,
lint,
first_arg_span,
&format!(
- "methods called `{}` usually take {}; consider choosing a less ambiguous name",
- conv,
+ "{} usually take {}",
+ suggestion,
&self_kinds
.iter()
.map(|k| k.description())
.collect::<Vec<_>>()
.join(" or ")
),
+ None,
+ "consider choosing a less ambiguous name",
);
}
}
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::LateContext;
use crate::consts::{constant_simple, Constant};
-use crate::utils::{match_def_path, match_trait_method, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_def_path, match_trait_method, paths};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::ty::implements_trait;
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_span::symbol::sym;
use crate::consts::{constant, Constant};
-use crate::utils::sugg::Sugg;
-use crate::utils::{
- get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_diagnostic_assoc_item, is_integer_const,
- iter_input_pats, last_path_segment, match_qpath, snippet, snippet_opt, span_lint, span_lint_and_sugg,
- span_lint_and_then, span_lint_hir_and_then, unsext, SpanlessEq,
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{
+ get_item_name, get_parent_expr, higher, in_constant, is_diagnostic_assoc_item, is_integer_const, iter_input_pats,
+ last_path_segment, match_qpath, unsext, SpanlessEq,
};
declare_clippy_lint! {
-use crate::utils::{snippet_opt, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_opt;
use rustc_ast::ast::{
BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability,
NodeId, Pat, PatKind, UnOp,
-use crate::utils::qualify_min_const_fn::is_min_const_fn;
-use crate::utils::{
- fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method,
-};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::qualify_min_const_fn::is_min_const_fn;
+use clippy_utils::ty::has_drop;
+use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method};
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
// [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415
//
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use if_chain::if_chain;
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
use rustc_ast::attr;
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use rustc_ast::ast;
use rustc_hir as hir;
use rustc_lint::{self, LateContext, LateLintPass, LintContext};
use crate::consts::{constant, Constant};
-use crate::utils::{sext, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sext;
use if_chain::if_chain;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
//! lint on multiple versions of a crate being used
-use crate::utils::{run_lints, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::run_lints;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_hir::{Crate, CRATE_HIR_ID};
use rustc_lint::{LateContext, LateLintPass};
-use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_def_path, paths, trait_ref_of_method};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TypeFoldable;
-use crate::utils::{higher, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::higher;
use rustc_hir as hir;
use rustc_hir::intravisit;
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Mutability};
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::subst::Subst;
-use crate::utils::{higher, is_direct_expn_of, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{higher, is_direct_expn_of};
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability};
use rustc_lint::{LateContext, LateLintPass};
//!
//! This lint is **warn** by default
-use crate::utils::{is_type_diagnostic_item, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_type_diagnostic_item;
use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
-use crate::utils::{in_macro, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
use if_chain::if_chain;
use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
use rustc_errors::Applicability;
//!
//! This lint is **warn** by default
-use crate::utils::sugg::Sugg;
-use crate::utils::{is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{is_expn_of, parent_node_is_if_expr};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
//!
//! This lint is **warn** by default
-use crate::utils::{is_automatically_derived, snippet_opt, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_automatically_derived;
+use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, Item, Mutability, Pat, PatKind};
-use crate::utils::{snippet_with_applicability, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_with_applicability;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind};
//! ```
//!
//! This lint is **warn** by default.
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::{indent_of, snippet, snippet_block};
use rustc_ast::ast;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{original_sp, DUMMY_SP};
use rustc_span::Span;
-use crate::utils::{indent_of, snippet, snippet_block, span_lint_and_help};
-
declare_clippy_lint! {
/// **What it does:** The lint checks for `if`-statements appearing in loops
/// that contain a `continue` statement in either their main blocks or their
-use crate::utils::ptr::get_spans;
-use crate::utils::{
- get_trait_def_id, implements_trait, is_copy, is_self, is_type_diagnostic_item, multispan_sugg, paths, snippet,
- snippet_opt, span_lint_and_then,
-};
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::ptr::get_spans;
+use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item};
+use clippy_utils::{get_trait_def_id, is_self, paths};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}
- fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) { }
+ fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
}
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{differing_macro_contexts, is_ok_ctor, is_some_ctor, meets_msrv};
+use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
-use crate::utils;
-use if_chain::if_chain;
-
declare_clippy_lint! {
/// **What it does:**
/// Suggests alternatives for useless applications of `?` in terminating expressions
SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner),
};
- utils::span_lint_and_sugg(
+ span_lint_and_sugg(
cx,
NEEDLESS_QUESTION_MARK,
entire_expr.span,
"question mark operator is useless here",
"try",
- format!("{}", utils::snippet(cx, inner_expr.span, r#""...""#)),
+ format!("{}", snippet(cx, inner_expr.span, r#""...""#)),
Applicability::MachineApplicable,
);
}
// Check outer expression matches CALL_IDENT(ARGUMENT) format
if let ExprKind::Call(path, args) = &expr.kind;
if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind;
- if utils::is_some_ctor(cx, path.res) || utils::is_ok_ctor(cx, path.res);
+ if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res);
// Extract inner expression from ARGUMENT
if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind;
// question mark operator
let inner_expr = &args[0];
+ // if the inner expr is inside macro but the outer one is not, do not lint (#6921)
+ if differing_macro_contexts(expr.span, inner_expr.span) {
+ return None;
+ }
+
let inner_ty = cx.typeck_results().expr_ty(inner_expr);
let outer_ty = cx.typeck_results().expr_ty(expr);
// Check if outer and inner type are Option
- let outer_is_some = utils::is_type_diagnostic_item(cx, outer_ty, sym::option_type);
- let inner_is_some = utils::is_type_diagnostic_item(cx, inner_ty, sym::option_type);
+ let outer_is_some = is_type_diagnostic_item(cx, outer_ty, sym::option_type);
+ let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type);
// Check for Option MSRV
- let meets_option_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV);
+ let meets_option_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV);
if outer_is_some && inner_is_some && meets_option_msrv {
return Some(SomeOkCall::SomeCall(expr, inner_expr));
}
// Check if outer and inner type are Result
- let outer_is_result = utils::is_type_diagnostic_item(cx, outer_ty, sym::result_type);
- let inner_is_result = utils::is_type_diagnostic_item(cx, inner_ty, sym::result_type);
+ let outer_is_result = is_type_diagnostic_item(cx, outer_ty, sym::result_type);
+ let inner_is_result = is_type_diagnostic_item(cx, inner_ty, sym::result_type);
// Additional check: if the error type of the Result can be converted
// via the From trait, then don't match
let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr);
// Must meet Result MSRV
- let meets_result_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV);
+ let meets_result_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV);
if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv {
return Some(SomeOkCall::OkCall(expr, inner_expr));
}
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{self, get_trait_def_id, paths};
use if_chain::if_chain;
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{self, paths, span_lint};
-
declare_clippy_lint! {
/// **What it does:**
/// Checks for the usage of negated comparison operators on types which only implement
let ty = cx.typeck_results().expr_ty(left);
let implements_ord = {
- if let Some(id) = utils::get_trait_def_id(cx, &paths::ORD) {
- utils::implements_trait(cx, ty, id, &[])
+ if let Some(id) = get_trait_def_id(cx, &paths::ORD) {
+ implements_trait(cx, ty, id, &[])
} else {
return;
}
let implements_partial_ord = {
if let Some(id) = cx.tcx.lang_items().partial_ord_trait() {
- utils::implements_trait(cx, ty, id, &[])
+ implements_trait(cx, ty, id, &[])
} else {
return;
}
+use clippy_utils::diagnostics::span_lint;
use if_chain::if_chain;
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_span::source_map::Span;
use crate::consts::{self, Constant};
-use crate::utils::span_lint;
declare_clippy_lint! {
/// **What it does:** Checks for multiplication by -1 as a form of negation.
-use crate::utils::paths;
-use crate::utils::sugg::DiagnosticBuilderExt;
-use crate::utils::{get_trait_def_id, return_ty, span_lint_hir_and_then};
+use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::paths;
+use clippy_utils::source::snippet;
+use clippy_utils::sugg::DiagnosticBuilderExt;
+use clippy_utils::{get_trait_def_id, return_ty};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
#[allow(clippy::too_many_lines)]
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
if let hir::ItemKind::Impl(hir::Impl {
- of_trait: None, items, ..
+ of_trait: None,
+ ref generics,
+ items,
+ ..
}) = item.kind
{
for assoc_item in items {
}
}
+ let generics_sugg = snippet(cx, generics.span, "");
span_lint_hir_and_then(
cx,
NEW_WITHOUT_DEFAULT,
cx,
item.span,
"try this",
- &create_new_without_default_suggest_msg(self_ty),
+ &create_new_without_default_suggest_msg(self_ty, &generics_sugg),
Applicability::MaybeIncorrect,
);
},
}
}
-fn create_new_without_default_suggest_msg(ty: Ty<'_>) -> String {
+fn create_new_without_default_suggest_msg(ty: Ty<'_>, generics_sugg: &str) -> String {
#[rustfmt::skip]
format!(
-"impl Default for {} {{
+"impl{} Default for {} {{
fn default() -> Self {{
Self::new()
}}
-}}", ty)
+}}", generics_sugg, ty)
}
-use crate::utils::{has_drop, snippet_opt, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::has_drop;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource};
use std::ptr;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::in_constant;
+use if_chain::if_chain;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{
use rustc_span::{InnerSpan, Span, DUMMY_SP};
use rustc_typeck::hir_ty_to_ty;
-use crate::utils::{in_constant, span_lint_and_then};
-use if_chain::if_chain;
-
// FIXME: this is a correctness problem but there's no suitable
// warn-by-default category.
declare_clippy_lint! {
fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
let substs = cx.typeck_results().node_substs(hir_id);
- let result = cx
- .tcx
- .const_eval_resolve(
- cx.param_env,
- ty::Unevaluated {
- def: ty::WithOptConstParam::unknown(def_id),
- substs,
- promoted: None
- },
- None
- );
+ let result = cx.tcx.const_eval_resolve(
+ cx.param_env,
+ ty::Unevaluated {
+ def: ty::WithOptConstParam::unknown(def_id),
+ substs,
+ promoted: None,
+ },
+ None,
+ );
is_value_unfrozen_raw(cx, result, ty)
}
-use crate::utils::{span_lint, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use rustc_ast::ast::{
Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat,
PatKind,
-use crate::utils::{match_type, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::paths;
+use clippy_utils::ty::match_type;
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
-use crate::utils::{is_direct_expn_of, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_direct_expn_of;
use if_chain::if_chain;
use rustc_ast::ast::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
-use crate::utils;
-use crate::utils::eager_or_lazy;
-use crate::utils::sugg::Sugg;
-use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::paths;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::usage::contains_return_break_continue_macro;
+use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, match_qpath};
use if_chain::if_chain;
-
use rustc_errors::Applicability;
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
/// If this is the else body of an if/else expression, then we need to wrap
/// it in curly braces. Otherwise, we don't.
fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| {
+ get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| {
let mut should_wrap = false;
if let Some(Expr {
expr: &'_ Expr<'tcx>,
) -> Option<OptionIfLetElseOccurence> {
if_chain! {
- if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly
+ if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly
if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind;
if arms.len() == 2;
if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already
if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind;
- if utils::match_qpath(struct_qpath, &paths::OPTION_SOME);
+ if match_qpath(struct_qpath, &paths::OPTION_SOME);
if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
- if !utils::usage::contains_return_break_continue_macro(arms[0].body);
- if !utils::usage::contains_return_break_continue_macro(arms[1].body);
+ if !contains_return_break_continue_macro(arms[0].body);
+ if !contains_return_break_continue_macro(arms[1].body);
then {
let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
let some_body = extract_body_from_arm(&arms[0])?;
-use crate::utils::{span_lint, SpanlessEq};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::SpanlessEq;
use if_chain::if_chain;
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
-use crate::utils::{find_macro_calls, is_type_diagnostic_item, return_ty, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{find_macro_calls, return_ty};
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_lint::{LateContext, LateLintPass};
-use crate::utils::{is_expn_of, match_panic_call, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{is_expn_of, match_panic_call};
use if_chain::if_chain;
use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass};
-use crate::utils::{is_automatically_derived, span_lint_hir};
+use clippy_utils::diagnostics::span_lint_hir;
+use clippy_utils::is_automatically_derived;
use if_chain::if_chain;
use rustc_hir::{Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use std::cmp;
-use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_self_ty;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_copy;
use if_chain::if_chain;
use rustc_ast::attr;
use rustc_errors::Applicability;
-use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use crate::utils::{last_path_segment, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::last_path_segment;
use rustc_hir::{
- intravisit, Body, Expr, ExprKind, PatField, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind,
+ intravisit, Body, Expr, ExprKind, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatField, PatKind,
QPath, Stmt, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
use if_chain::if_chain;
use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp};
use rustc_errors::Applicability;
//! Checks for usage of `&Vec[_]` and `&String`.
-use crate::utils::ptr::get_spans;
-use crate::utils::{
- is_allowed, is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg,
- span_lint_and_then, walk_ptrs_hir_ty,
-};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::ptr::get_spans;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty};
+use clippy_utils::{is_allowed, match_qpath, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
-use crate::utils;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
impl LateLintPass<'_> for PtrEq {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if utils::in_macro(expr.span) {
+ if in_macro(expr.span) {
return;
}
if_chain! {
if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left);
if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right);
- if let Some(left_snip) = utils::snippet_opt(cx, left_var.span);
- if let Some(right_snip) = utils::snippet_opt(cx, right_var.span);
+ if let Some(left_snip) = snippet_opt(cx, left_var.span);
+ if let Some(right_snip) = snippet_opt(cx, right_var.span);
then {
- utils::span_lint_and_sugg(
+ span_lint_and_sugg(
cx,
PTR_EQ,
expr.span,
-use crate::utils::{snippet_opt, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::source::snippet_opt;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{eq_expr_value, match_def_path, match_qpath, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
-use crate::utils::sugg::Sugg;
-use crate::utils::{
- eq_expr_value, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability,
- span_lint_and_sugg,
-};
-
declare_clippy_lint! {
/// **What it does:** Checks for expressions that could be replaced by the question mark operator.
///
use crate::consts::{constant, Constant};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path};
+use clippy_utils::{higher, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
use rustc_span::symbol::Ident;
use std::cmp::Ordering;
-use crate::utils::sugg::Sugg;
-use crate::utils::{
- get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt,
- snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then,
-};
-use crate::utils::{higher, SpanlessEq};
-
declare_clippy_lint! {
/// **What it does:** Checks for zipping a collection with the range of
/// `0.._.len()`.
-use crate::utils::{
- fn_has_unsatisfiable_preds, has_drop, is_copy, is_type_diagnostic_item, match_def_path, paths, snippet_opt,
- span_lint_hir, span_lint_hir_and_then, walk_ptrs_ty_depth,
-};
+use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth};
+use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
use if_chain::if_chain;
use rustc_data_structures::{fx::FxHashMap, transitive_relation::TransitiveRelation};
use rustc_errors::Applicability;
mir::Operand::Copy(p) | mir::Operand::Move(p) => {
self.possible_borrower.add(p.local, *dest);
},
- _ => (),
+ mir::Operand::Constant(..) => (),
}
}
}
let mut visit_op = |op: &mir::Operand<'_>| match op {
mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local),
- _ => (),
+ mir::Operand::Constant(..) => (),
};
match rvalue {
-use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::source::snippet_with_applicability;
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_ast::visit as ast_visit;
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind};
use rustc_ast::visit::{walk_expr, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass};
-use crate::utils::{meets_msrv, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::meets_msrv;
use rustc_ast::ast::{Expr, ExprKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
-use crate::utils::span_lint_and_then;
+use clippy_utils::diagnostics::span_lint_and_then;
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind, VisibilityKind};
use rustc_lint::{LateContext, LateLintPass};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_lang_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem};
use rustc_middle::{lint::in_external_macro, ty::TyS};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{is_type_lang_item, snippet_with_applicability, span_lint_and_sugg};
-
declare_clippy_lint! {
/// **What it does:** Checks for redundant slicing expressions which use the full range, and
/// do not change the type.
-use crate::utils::{meets_msrv, snippet, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::meets_msrv;
+use clippy_utils::source::snippet;
use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
-use crate::utils::{last_path_segment, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::last_path_segment;
+use clippy_utils::source::snippet;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
use rustc_hir::{GenericArg, Mutability, Ty, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-
declare_clippy_lint! {
/// **What it does:** Checks for usage of `&Option<&T>`.
///
-use crate::utils::sugg::Sugg;
-use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
use rustc_errors::Applicability;
use crate::consts::{constant, Constant};
-use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_ast::ast::{LitKind, StrStyle};
use rustc_data_structures::fx::FxHashSet;
use crate::consts::{constant_context, Constant};
-use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{fn_def_id, in_macro, match_qpath};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
use rustc_errors::Applicability;
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};
-
declare_clippy_lint! {
/// **What it does:** Checks for `let`-bindings, which are subsequently
/// returned.
-use crate::utils::{eq_expr_value, snippet, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::eq_expr_value;
+use clippy_utils::source::snippet;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{in_macro, snippet_with_macro_callsite, span_lint_and_sugg, sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::{in_macro, sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Block, ExprKind};
-use crate::utils::{get_trait_def_id, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{get_trait_def_id, paths};
use rustc_hir::{Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::{contains_name, higher, iter_input_pats};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind,
-use crate::utils::{in_macro, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
use if_chain::if_chain;
use rustc_ast::{Item, ItemKind, UseTreeKind};
use rustc_errors::Applicability;
//! Lint on use of `size_of` or `size_of_val` of T in an expression
//! expecting a count of T
-use crate::utils::{match_def_path, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_hir::BinOpKind;
use rustc_hir::{Expr, ExprKind};
-use crate::utils::sugg::Sugg;
-use crate::utils::{get_enclosing_block, match_qpath, span_lint_and_then, SpanlessEq};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{get_enclosing_block, match_qpath, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use crate::utils::{is_slice_of_primitives, span_lint_and_then, sugg::Sugg};
-
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{is_slice_of_primitives, sugg::Sugg};
use if_chain::if_chain;
-
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::SpanlessEq;
+use clippy_utils::{get_parent_expr, is_allowed, match_function_call, method_calls, paths};
+use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_span::source_map::Spanned;
use rustc_span::sym;
-use if_chain::if_chain;
-
-use crate::utils::SpanlessEq;
-use crate::utils::{
- get_parent_expr, is_allowed, is_type_diagnostic_item, match_function_call, method_calls, paths, span_lint,
- span_lint_and_help, span_lint_and_sugg,
-};
-
declare_clippy_lint! {
/// **What it does:** Checks for string appends of the form `x = x + y` (without
/// `let`!).
impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
- use crate::utils::{snippet, snippet_with_applicability};
use rustc_ast::LitKind;
if_chain! {
}
}
}
+
+ if_chain! {
+ if let ExprKind::MethodCall(path, _, [recv], _) = &e.kind;
+ if path.ident.name == sym!(into_bytes);
+ if let ExprKind::MethodCall(path, _, [recv], _) = &recv.kind;
+ if matches!(&*path.ident.name.as_str(), "to_owned" | "to_string");
+ if let ExprKind::Lit(lit) = &recv.kind;
+ if let LitKind::Str(lit_content, _) = &lit.node;
+
+ if lit_content.as_str().is_ascii();
+ if lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT;
+ if !recv.span.from_expansion();
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+
+ span_lint_and_sugg(
+ cx,
+ STRING_LIT_AS_BYTES,
+ e.span,
+ "calling `into_bytes()` on a string literal",
+ "consider using a byte string literal instead",
+ format!(
+ "b{}.to_vec()",
+ snippet_with_applicability(cx, recv.span, r#""..""#, &mut applicability)
+ ),
+ applicability,
+ );
+ }
+ }
}
}
-use crate::utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter};
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
use core::ops::{Add, AddAssign};
use if_chain::if_chain;
use rustc_ast::ast::{BinOpKind, Expr, ExprKind, StmtKind};
-use crate::utils::{get_trait_def_id, span_lint, trait_ref_of_method};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{get_trait_def_id, paths, trait_ref_of_method};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
expected_ops: &[hir::BinOpKind],
) -> Option<&'static str> {
let mut trait_ids = vec![];
- let [krate, module] = crate::utils::paths::OPS_MODULE;
+ let [krate, module] = paths::OPS_MODULE;
for &t in traits {
let path = [krate, module, t];
-use crate::utils::sugg::Sugg;
-use crate::utils::{
- differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then,
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{differing_macro_contexts, eq_expr_value};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind};
-use crate::utils::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
-use crate::utils::{is_adjusted, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_adjusted;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{match_def_path, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::match_def_path;
+use clippy_utils::source::snippet_with_applicability;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use crate::utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
-use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::{in_macro, SpanlessHash};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use super::CROSSPOINTER_TRANSMUTE;
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
mod utils;
mod wrong_transmute;
-use crate::utils::{in_constant, match_def_path, paths};
+use clippy_utils::{in_constant, match_def_path, paths};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use super::TRANSMUTE_FLOAT_TO_INT;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
use if_chain::if_chain;
use rustc_ast as ast;
use rustc_errors::Applicability;
use super::TRANSMUTE_INT_TO_BOOL;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
use rustc_ast as ast;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use super::TRANSMUTE_INT_TO_CHAR;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
use rustc_ast as ast;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use super::TRANSMUTE_INT_TO_FLOAT;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use super::TRANSMUTE_PTR_TO_PTR;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use super::utils::get_type_snippet;
use super::TRANSMUTE_PTR_TO_REF;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability, QPath};
use rustc_lint::LateContext;
use super::{TRANSMUTE_BYTES_TO_STR, TRANSMUTE_PTR_TO_PTR};
-use crate::utils::{snippet, span_lint_and_sugg, span_lint_and_then, sugg};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::sugg;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability};
use super::utils::can_be_expressed_as_pointer_cast;
use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use super::utils::is_layout_incompatible;
use super::UNSOUND_COLLECTION_TRANSMUTE;
-use crate::utils::{match_def_path, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_def_path, paths};
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use super::USELESS_TRANSMUTE;
-use crate::utils::{span_lint, span_lint_and_then, sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::sugg;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
-use crate::utils::{is_normalizable, last_path_segment, snippet};
+use clippy_utils::last_path_segment;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_normalizable;
use if_chain::if_chain;
use rustc_hir::{Expr, GenericArg, QPath, TyKind};
use rustc_lint::LateContext;
use super::WRONG_TRANSMUTE;
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use crate::consts::{constant_context, Constant};
-use crate::utils::{match_qpath, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_qpath, paths};
use if_chain::if_chain;
use rustc_ast::LitKind;
use rustc_hir::{Expr, ExprKind};
-use crate::utils::{
- differing_macro_contexts, in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet,
- snippet_with_macro_callsite, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{snippet, snippet_with_macro_callsite};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::{match_path, paths};
+use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
self as hir, GenericArg, GenericBounds, GenericParamKind, HirId, Lifetime, MutTy, Mutability, Node, QPath,
};
use rustc_lint::LateContext;
-use if_chain::if_chain;
-
-use crate::utils::{match_path, paths, snippet, span_lint_and_sugg};
-
use super::BORROWED_BOX;
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, mut_ty: &MutTy<'_>) -> bool {
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_ty_param_diagnostic_item;
use rustc_hir::{self as hir, def_id::DefId, QPath};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
-use crate::utils::{is_ty_param_diagnostic_item, span_lint_and_help};
-
use super::BOX_VEC;
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{match_def_path, paths};
use rustc_hir::{self as hir, def_id::DefId};
use rustc_lint::LateContext;
-use crate::utils::{match_def_path, paths, span_lint_and_help};
-
use super::LINKEDLIST;
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, def_id: DefId) -> bool {
use std::cmp::Ordering;
use std::collections::BTreeMap;
+use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_help, span_lint_and_then};
+use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::ty::{is_isize_or_usize, is_type_diagnostic_item};
use if_chain::if_chain;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::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, Local, MatchSource, MutTy, Node, QPath, Stmt, StmtKind, TraitFn, TraitItem,
- TraitItemKind, TyKind,
+ BinOpKind, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
+ ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitFn, TraitItem, TraitItemKind, TyKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, IntTy, Ty, TyS, TypeckResults, UintTy};
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
-use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::Span;
use rustc_span::symbol::sym;
use rustc_target::abi::LayoutOf;
use rustc_typeck::hir_ty_to_ty;
use crate::consts::{constant, Constant};
-use crate::utils::paths;
-use crate::utils::{
- clip, comparisons, differing_macro_contexts, higher, indent_of, int_bits, is_isize_or_usize,
- is_type_diagnostic_item, match_path, multispan_sugg, reindent_multiline, sext, snippet, snippet_opt,
- snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_then, unsext,
-};
+use clippy_utils::paths;
+use clippy_utils::{clip, comparisons, differing_macro_contexts, int_bits, match_path, sext, unsext};
declare_clippy_lint! {
/// **What it does:** Checks for use of `Box<Vec<_>>` anywhere in the code.
match item.kind {
TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_ty(cx, ty, false),
TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, &sig.decl),
- _ => (),
+ TraitItemKind::Type(..) => (),
}
}
}
}
-declare_clippy_lint! {
- /// **What it does:** Checks for binding a unit value.
- ///
- /// **Why is this bad?** A unit value cannot usefully be used anywhere. So
- /// binding one is kind of pointless.
- ///
- /// **Known problems:** None.
- ///
- /// **Example:**
- /// ```rust
- /// let x = {
- /// 1;
- /// };
- /// ```
- pub LET_UNIT_VALUE,
- pedantic,
- "creating a `let` binding to a value of unit type, which usually can't be used afterwards"
-}
-
-declare_lint_pass!(LetUnitValue => [LET_UNIT_VALUE]);
-
-impl<'tcx> LateLintPass<'tcx> for LetUnitValue {
- fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
- if let StmtKind::Local(ref local) = stmt.kind {
- if is_unit(cx.typeck_results().pat_ty(&local.pat)) {
- if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() {
- return;
- }
- if higher::is_from_for_desugar(local) {
- return;
- }
- span_lint_and_then(
- cx,
- LET_UNIT_VALUE,
- stmt.span,
- "this let-binding has unit value",
- |diag| {
- if let Some(expr) = &local.init {
- let snip = snippet_with_macro_callsite(cx, expr.span, "()");
- diag.span_suggestion(
- stmt.span,
- "omit the `let` binding",
- format!("{};", snip),
- Applicability::MachineApplicable, // snippet
- );
- }
- },
- );
- }
- }
- }
-}
-
-declare_clippy_lint! {
- /// **What it does:** Checks for comparisons to unit. This includes all binary
- /// comparisons (like `==` and `<`) and asserts.
- ///
- /// **Why is this bad?** Unit is always equal to itself, and thus is just a
- /// clumsily written constant. Mostly this happens when someone accidentally
- /// adds semicolons at the end of the operands.
- ///
- /// **Known problems:** None.
- ///
- /// **Example:**
- /// ```rust
- /// # fn foo() {};
- /// # fn bar() {};
- /// # fn baz() {};
- /// if {
- /// foo();
- /// } == {
- /// bar();
- /// } {
- /// baz();
- /// }
- /// ```
- /// is equal to
- /// ```rust
- /// # fn foo() {};
- /// # fn bar() {};
- /// # fn baz() {};
- /// {
- /// foo();
- /// bar();
- /// baz();
- /// }
- /// ```
- ///
- /// For asserts:
- /// ```rust
- /// # fn foo() {};
- /// # fn bar() {};
- /// assert_eq!({ foo(); }, { bar(); });
- /// ```
- /// will always succeed
- pub UNIT_CMP,
- correctness,
- "comparing unit values"
-}
-
-declare_lint_pass!(UnitCmp => [UNIT_CMP]);
-
-impl<'tcx> LateLintPass<'tcx> for UnitCmp {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- if expr.span.from_expansion() {
- if let Some(callee) = expr.span.source_callee() {
- if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind {
- if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind {
- let op = cmp.node;
- if op.is_comparison() && is_unit(cx.typeck_results().expr_ty(left)) {
- let result = match &*symbol.as_str() {
- "assert_eq" | "debug_assert_eq" => "succeed",
- "assert_ne" | "debug_assert_ne" => "fail",
- _ => return,
- };
- span_lint(
- cx,
- UNIT_CMP,
- expr.span,
- &format!(
- "`{}` of unit values detected. This will always {}",
- symbol.as_str(),
- result
- ),
- );
- }
- }
- }
- }
- return;
- }
- if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind {
- let op = cmp.node;
- if op.is_comparison() && is_unit(cx.typeck_results().expr_ty(left)) {
- let result = match op {
- BinOpKind::Eq | BinOpKind::Le | BinOpKind::Ge => "true",
- _ => "false",
- };
- span_lint(
- cx,
- UNIT_CMP,
- expr.span,
- &format!(
- "{}-comparison of unit values detected. This will always be {}",
- op.as_str(),
- result
- ),
- );
- }
- }
- }
-}
-
-declare_clippy_lint! {
- /// **What it does:** Checks for passing a unit value as an argument to a function without using a
- /// unit literal (`()`).
- ///
- /// **Why is this bad?** This is likely the result of an accidental semicolon.
- ///
- /// **Known problems:** None.
- ///
- /// **Example:**
- /// ```rust,ignore
- /// foo({
- /// let a = bar();
- /// baz(a);
- /// })
- /// ```
- pub UNIT_ARG,
- complexity,
- "passing unit to a function"
-}
-
-declare_lint_pass!(UnitArg => [UNIT_ARG]);
-
-impl<'tcx> LateLintPass<'tcx> for UnitArg {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if expr.span.from_expansion() {
- return;
- }
-
- // apparently stuff in the desugaring of `?` can trigger this
- // so check for that here
- // only the calls to `Try::from_error` is marked as desugared,
- // so we need to check both the current Expr and its parent.
- if is_questionmark_desugar_marked_call(expr) {
- return;
- }
- if_chain! {
- let map = &cx.tcx.hir();
- let opt_parent_node = map.find(map.get_parent_node(expr.hir_id));
- if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node;
- if is_questionmark_desugar_marked_call(parent_expr);
- then {
- return;
- }
- }
-
- match expr.kind {
- ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => {
- let args_to_recover = args
- .iter()
- .filter(|arg| {
- if is_unit(cx.typeck_results().expr_ty(arg)) && !is_unit_literal(arg) {
- !matches!(
- &arg.kind,
- ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..)
- )
- } else {
- false
- }
- })
- .collect::<Vec<_>>();
- if !args_to_recover.is_empty() {
- lint_unit_args(cx, expr, &args_to_recover);
- }
- },
- _ => (),
- }
- }
-}
-
-fn fmt_stmts_and_call(
- cx: &LateContext<'_>,
- call_expr: &Expr<'_>,
- call_snippet: &str,
- args_snippets: &[impl AsRef<str>],
- non_empty_block_args_snippets: &[impl AsRef<str>],
-) -> String {
- let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0);
- let call_snippet_with_replacements = args_snippets
- .iter()
- .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1));
-
- let mut stmts_and_call = non_empty_block_args_snippets
- .iter()
- .map(|it| it.as_ref().to_owned())
- .collect::<Vec<_>>();
- stmts_and_call.push(call_snippet_with_replacements);
- stmts_and_call = stmts_and_call
- .into_iter()
- .map(|v| reindent_multiline(v.into(), true, Some(call_expr_indent)).into_owned())
- .collect();
-
- let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent)));
- // expr is not in a block statement or result expression position, wrap in a block
- let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id));
- if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) {
- let block_indent = call_expr_indent + 4;
- stmts_and_call_snippet =
- reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned();
- stmts_and_call_snippet = format!(
- "{{\n{}{}\n{}}}",
- " ".repeat(block_indent),
- &stmts_and_call_snippet,
- " ".repeat(call_expr_indent)
- );
- }
- stmts_and_call_snippet
-}
-
-fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) {
- let mut applicability = Applicability::MachineApplicable;
- let (singular, plural) = if args_to_recover.len() > 1 {
- ("", "s")
- } else {
- ("a ", "")
- };
- span_lint_and_then(
- cx,
- UNIT_ARG,
- expr.span,
- &format!("passing {}unit value{} to a function", singular, plural),
- |db| {
- let mut or = "";
- args_to_recover
- .iter()
- .filter_map(|arg| {
- if_chain! {
- if let ExprKind::Block(block, _) = arg.kind;
- if block.expr.is_none();
- if let Some(last_stmt) = block.stmts.iter().last();
- if let StmtKind::Semi(last_expr) = last_stmt.kind;
- if let Some(snip) = snippet_opt(cx, last_expr.span);
- then {
- Some((
- last_stmt.span,
- snip,
- ))
- }
- else {
- None
- }
- }
- })
- .for_each(|(span, sugg)| {
- db.span_suggestion(
- span,
- "remove the semicolon from the last statement in the block",
- sugg,
- Applicability::MaybeIncorrect,
- );
- or = "or ";
- applicability = Applicability::MaybeIncorrect;
- });
-
- let arg_snippets: Vec<String> = args_to_recover
- .iter()
- .filter_map(|arg| snippet_opt(cx, arg.span))
- .collect();
- let arg_snippets_without_empty_blocks: Vec<String> = args_to_recover
- .iter()
- .filter(|arg| !is_empty_block(arg))
- .filter_map(|arg| snippet_opt(cx, arg.span))
- .collect();
-
- if let Some(call_snippet) = snippet_opt(cx, expr.span) {
- let sugg = fmt_stmts_and_call(
- cx,
- expr,
- &call_snippet,
- &arg_snippets,
- &arg_snippets_without_empty_blocks,
- );
-
- if arg_snippets_without_empty_blocks.is_empty() {
- db.multipart_suggestion(
- &format!("use {}unit literal{} instead", singular, plural),
- args_to_recover
- .iter()
- .map(|arg| (arg.span, "()".to_string()))
- .collect::<Vec<_>>(),
- applicability,
- );
- } else {
- let plural = arg_snippets_without_empty_blocks.len() > 1;
- let empty_or_s = if plural { "s" } else { "" };
- let it_or_them = if plural { "them" } else { "it" };
- db.span_suggestion(
- expr.span,
- &format!(
- "{}move the expression{} in front of the call and replace {} with the unit literal `()`",
- or, empty_or_s, it_or_them
- ),
- sugg,
- applicability,
- );
- }
- }
- },
- );
-}
-
-fn is_empty_block(expr: &Expr<'_>) -> bool {
- matches!(
- expr.kind,
- ExprKind::Block(
- Block {
- stmts: &[],
- expr: None,
- ..
- },
- _,
- )
- )
-}
-
-fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool {
- use rustc_span::hygiene::DesugaringKind;
- if let ExprKind::Call(ref callee, _) = expr.kind {
- callee.span.is_desugaring(DesugaringKind::QuestionMark)
- } else {
- false
- }
-}
-
-fn is_unit(ty: Ty<'_>) -> bool {
- matches!(ty.kind(), ty::Tuple(slice) if slice.is_empty())
-}
-
-fn is_unit_literal(expr: &Expr<'_>) -> bool {
- matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty())
-}
-
declare_clippy_lint! {
/// **What it does:** Checks for types used in structs, parameters and `let`
/// declarations above a certain complexity threshold.
TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_type(cx, ty),
TraitItemKind::Fn(FnSig { ref decl, .. }, TraitFn::Required(_)) => self.check_fndecl(cx, decl),
// methods with default impl are covered by check_fn
- _ => (),
+ TraitItemKind::Type(..) | TraitItemKind::Fn(_, TraitFn::Provided(_)) => (),
}
}
match item.kind {
ImplItemKind::Const(ref ty, _) | ImplItemKind::TyAlias(ref ty) => self.check_type(cx, ty),
// methods are covered by check_fn
- _ => (),
+ ImplItemKind::Fn(..) => (),
}
}
// function types bring a lot of overhead
TyKind::BareFn(ref bare) if bare.abi == Abi::Rust => (50 * self.nest, 1),
- TyKind::TraitObject(ref param_bounds, ..) => {
+ TyKind::TraitObject(ref param_bounds, _, _) => {
let has_lifetime_parameters = param_bounds.iter().any(|bound| {
bound
.bound_generic_params
) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> {
use crate::types::AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible};
use crate::types::ExtremeType::{Maximum, Minimum};
- use crate::utils::comparisons::{normalize_comparison, Rel};
+ use clippy_utils::comparisons::{normalize_comparison, Rel};
// absurd comparison only makes sense on primitive types
// primitive types don't implement comparison operators with each other
rhs: &'tcx Expr<'_>,
invert: bool,
) {
- use crate::utils::comparisons::Rel;
+ use clippy_utils::comparisons::Rel;
if let Some((lb, ub)) = lhs_bounds {
if let Some(norm_rhs_val) = node_as_const_fullint(cx, rhs) {
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_ty_param_diagnostic_item;
use rustc_hir::{self as hir, def_id::DefId, QPath};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
-use crate::utils::{is_ty_param_diagnostic_item, span_lint};
-
use super::OPTION_OPTION;
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item};
use rustc_errors::Applicability;
use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
-use crate::utils::{
- get_qpath_generic_tys, is_ty_param_diagnostic_item, snippet_with_applicability, span_lint_and_sugg,
-};
-
use super::RC_BUFFER;
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item};
use rustc_errors::Applicability;
use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
-use crate::utils::{
- get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item, snippet_with_applicability,
- span_lint_and_sugg,
-};
-
use super::{utils, REDUNDANT_ALLOCATION};
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
+use clippy_utils::last_path_segment;
+use if_chain::if_chain;
use rustc_hir::{GenericArg, QPath, TyKind};
use rustc_lint::LateContext;
use rustc_span::source_map::Span;
-use crate::utils::last_path_segment;
-
-use if_chain::if_chain;
-
pub(super) fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Span> {
let last = last_path_segment(qpath);
if_chain! {
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::last_path_segment;
+use clippy_utils::source::snippet;
+use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{self as hir, def_id::DefId, GenericArg, QPath, TyKind};
use rustc_lint::LateContext;
use rustc_target::abi::LayoutOf;
use rustc_typeck::hir_ty_to_ty;
-use if_chain::if_chain;
-
-use crate::utils::{last_path_segment, snippet, span_lint_and_sugg};
-
use super::VEC_BOX;
pub(super) fn check(
-use crate::utils::{is_type_lang_item, match_function_call, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_lang_item;
+use clippy_utils::{match_function_call, paths};
use rustc_hir::{lang_items, Expr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{is_allowed, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_allowed;
+use clippy_utils::source::snippet;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, HirId};
-use crate::utils::{get_trait_def_id, paths, span_lint, span_lint_and_help};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::{get_trait_def_id, paths};
use if_chain::if_chain;
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, ExprKind, StmtKind};
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::higher;
+use clippy_utils::source::snippet_with_macro_callsite;
+use rustc_errors::Applicability;
+use rustc_hir::{Stmt, StmtKind};
+use rustc_lint::{LateContext, LintContext};
+use rustc_middle::lint::in_external_macro;
+
+use super::LET_UNIT_VALUE;
+
+pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
+ if let StmtKind::Local(ref local) = stmt.kind {
+ if cx.typeck_results().pat_ty(&local.pat).is_unit() {
+ if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() {
+ return;
+ }
+ if higher::is_from_for_desugar(local) {
+ return;
+ }
+ span_lint_and_then(
+ cx,
+ LET_UNIT_VALUE,
+ stmt.span,
+ "this let-binding has unit value",
+ |diag| {
+ if let Some(expr) = &local.init {
+ let snip = snippet_with_macro_callsite(cx, expr.span, "()");
+ diag.span_suggestion(
+ stmt.span,
+ "omit the `let` binding",
+ format!("{};", snip),
+ Applicability::MachineApplicable, // snippet
+ );
+ }
+ },
+ );
+ }
+ }
+}
--- /dev/null
+mod let_unit_value;
+mod unit_arg;
+mod unit_cmp;
+mod utils;
+
+use rustc_hir::{Expr, Stmt};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for binding a unit value.
+ ///
+ /// **Why is this bad?** A unit value cannot usefully be used anywhere. So
+ /// binding one is kind of pointless.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// let x = {
+ /// 1;
+ /// };
+ /// ```
+ pub LET_UNIT_VALUE,
+ pedantic,
+ "creating a `let` binding to a value of unit type, which usually can't be used afterwards"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for comparisons to unit. This includes all binary
+ /// comparisons (like `==` and `<`) and asserts.
+ ///
+ /// **Why is this bad?** Unit is always equal to itself, and thus is just a
+ /// clumsily written constant. Mostly this happens when someone accidentally
+ /// adds semicolons at the end of the operands.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// # fn foo() {};
+ /// # fn bar() {};
+ /// # fn baz() {};
+ /// if {
+ /// foo();
+ /// } == {
+ /// bar();
+ /// } {
+ /// baz();
+ /// }
+ /// ```
+ /// is equal to
+ /// ```rust
+ /// # fn foo() {};
+ /// # fn bar() {};
+ /// # fn baz() {};
+ /// {
+ /// foo();
+ /// bar();
+ /// baz();
+ /// }
+ /// ```
+ ///
+ /// For asserts:
+ /// ```rust
+ /// # fn foo() {};
+ /// # fn bar() {};
+ /// assert_eq!({ foo(); }, { bar(); });
+ /// ```
+ /// will always succeed
+ pub UNIT_CMP,
+ correctness,
+ "comparing unit values"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for passing a unit value as an argument to a function without using a
+ /// unit literal (`()`).
+ ///
+ /// **Why is this bad?** This is likely the result of an accidental semicolon.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust,ignore
+ /// foo({
+ /// let a = bar();
+ /// baz(a);
+ /// })
+ /// ```
+ pub UNIT_ARG,
+ complexity,
+ "passing unit to a function"
+}
+
+declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]);
+
+impl LateLintPass<'_> for UnitTypes {
+ fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
+ let_unit_value::check(cx, stmt);
+ }
+
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ unit_cmp::check(cx, expr);
+ unit_arg::check(cx, expr);
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{self as hir, Block, Expr, ExprKind, MatchSource, Node, StmtKind};
+use rustc_lint::LateContext;
+
+use super::{utils, UNIT_ARG};
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if expr.span.from_expansion() {
+ return;
+ }
+
+ // apparently stuff in the desugaring of `?` can trigger this
+ // so check for that here
+ // only the calls to `Try::from_error` is marked as desugared,
+ // so we need to check both the current Expr and its parent.
+ if is_questionmark_desugar_marked_call(expr) {
+ return;
+ }
+ if_chain! {
+ let map = &cx.tcx.hir();
+ let opt_parent_node = map.find(map.get_parent_node(expr.hir_id));
+ if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node;
+ if is_questionmark_desugar_marked_call(parent_expr);
+ then {
+ return;
+ }
+ }
+
+ match expr.kind {
+ ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => {
+ let args_to_recover = args
+ .iter()
+ .filter(|arg| {
+ if cx.typeck_results().expr_ty(arg).is_unit() && !utils::is_unit_literal(arg) {
+ !matches!(
+ &arg.kind,
+ ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..)
+ )
+ } else {
+ false
+ }
+ })
+ .collect::<Vec<_>>();
+ if !args_to_recover.is_empty() {
+ lint_unit_args(cx, expr, &args_to_recover);
+ }
+ },
+ _ => (),
+ }
+}
+
+fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool {
+ use rustc_span::hygiene::DesugaringKind;
+ if let ExprKind::Call(ref callee, _) = expr.kind {
+ callee.span.is_desugaring(DesugaringKind::QuestionMark)
+ } else {
+ false
+ }
+}
+
+fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) {
+ let mut applicability = Applicability::MachineApplicable;
+ let (singular, plural) = if args_to_recover.len() > 1 {
+ ("", "s")
+ } else {
+ ("a ", "")
+ };
+ span_lint_and_then(
+ cx,
+ UNIT_ARG,
+ expr.span,
+ &format!("passing {}unit value{} to a function", singular, plural),
+ |db| {
+ let mut or = "";
+ args_to_recover
+ .iter()
+ .filter_map(|arg| {
+ if_chain! {
+ if let ExprKind::Block(block, _) = arg.kind;
+ if block.expr.is_none();
+ if let Some(last_stmt) = block.stmts.iter().last();
+ if let StmtKind::Semi(last_expr) = last_stmt.kind;
+ if let Some(snip) = snippet_opt(cx, last_expr.span);
+ then {
+ Some((
+ last_stmt.span,
+ snip,
+ ))
+ }
+ else {
+ None
+ }
+ }
+ })
+ .for_each(|(span, sugg)| {
+ db.span_suggestion(
+ span,
+ "remove the semicolon from the last statement in the block",
+ sugg,
+ Applicability::MaybeIncorrect,
+ );
+ or = "or ";
+ applicability = Applicability::MaybeIncorrect;
+ });
+
+ let arg_snippets: Vec<String> = args_to_recover
+ .iter()
+ .filter_map(|arg| snippet_opt(cx, arg.span))
+ .collect();
+ let arg_snippets_without_empty_blocks: Vec<String> = args_to_recover
+ .iter()
+ .filter(|arg| !is_empty_block(arg))
+ .filter_map(|arg| snippet_opt(cx, arg.span))
+ .collect();
+
+ if let Some(call_snippet) = snippet_opt(cx, expr.span) {
+ let sugg = fmt_stmts_and_call(
+ cx,
+ expr,
+ &call_snippet,
+ &arg_snippets,
+ &arg_snippets_without_empty_blocks,
+ );
+
+ if arg_snippets_without_empty_blocks.is_empty() {
+ db.multipart_suggestion(
+ &format!("use {}unit literal{} instead", singular, plural),
+ args_to_recover
+ .iter()
+ .map(|arg| (arg.span, "()".to_string()))
+ .collect::<Vec<_>>(),
+ applicability,
+ );
+ } else {
+ let plural = arg_snippets_without_empty_blocks.len() > 1;
+ let empty_or_s = if plural { "s" } else { "" };
+ let it_or_them = if plural { "them" } else { "it" };
+ db.span_suggestion(
+ expr.span,
+ &format!(
+ "{}move the expression{} in front of the call and replace {} with the unit literal `()`",
+ or, empty_or_s, it_or_them
+ ),
+ sugg,
+ applicability,
+ );
+ }
+ }
+ },
+ );
+}
+
+fn is_empty_block(expr: &Expr<'_>) -> bool {
+ matches!(
+ expr.kind,
+ ExprKind::Block(
+ Block {
+ stmts: &[],
+ expr: None,
+ ..
+ },
+ _,
+ )
+ )
+}
+
+fn fmt_stmts_and_call(
+ cx: &LateContext<'_>,
+ call_expr: &Expr<'_>,
+ call_snippet: &str,
+ args_snippets: &[impl AsRef<str>],
+ non_empty_block_args_snippets: &[impl AsRef<str>],
+) -> String {
+ let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0);
+ let call_snippet_with_replacements = args_snippets
+ .iter()
+ .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1));
+
+ let mut stmts_and_call = non_empty_block_args_snippets
+ .iter()
+ .map(|it| it.as_ref().to_owned())
+ .collect::<Vec<_>>();
+ stmts_and_call.push(call_snippet_with_replacements);
+ stmts_and_call = stmts_and_call
+ .into_iter()
+ .map(|v| reindent_multiline(v.into(), true, Some(call_expr_indent)).into_owned())
+ .collect();
+
+ let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent)));
+ // expr is not in a block statement or result expression position, wrap in a block
+ let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id));
+ if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) {
+ let block_indent = call_expr_indent + 4;
+ stmts_and_call_snippet =
+ reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned();
+ stmts_and_call_snippet = format!(
+ "{{\n{}{}\n{}}}",
+ " ".repeat(block_indent),
+ &stmts_and_call_snippet,
+ " ".repeat(call_expr_indent)
+ );
+ }
+ stmts_and_call_snippet
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::hygiene::{ExpnKind, MacroKind};
+
+use super::UNIT_CMP;
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if expr.span.from_expansion() {
+ if let Some(callee) = expr.span.source_callee() {
+ if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind {
+ if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind {
+ let op = cmp.node;
+ if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() {
+ let result = match &*symbol.as_str() {
+ "assert_eq" | "debug_assert_eq" => "succeed",
+ "assert_ne" | "debug_assert_ne" => "fail",
+ _ => return,
+ };
+ span_lint(
+ cx,
+ UNIT_CMP,
+ expr.span,
+ &format!(
+ "`{}` of unit values detected. This will always {}",
+ symbol.as_str(),
+ result
+ ),
+ );
+ }
+ }
+ }
+ }
+ return;
+ }
+
+ if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind {
+ let op = cmp.node;
+ if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() {
+ let result = match op {
+ BinOpKind::Eq | BinOpKind::Le | BinOpKind::Ge => "true",
+ _ => "false",
+ };
+ span_lint(
+ cx,
+ UNIT_CMP,
+ expr.span,
+ &format!(
+ "{}-comparison of unit values detected. This will always be {}",
+ op.as_str(),
+ result
+ ),
+ );
+ }
+ }
+}
--- /dev/null
+use rustc_hir::{Expr, ExprKind};
+
+pub(super) fn is_unit_literal(expr: &Expr<'_>) -> bool {
+ matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty())
+}
-use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
-use crate::utils;
-use crate::utils::sugg::Sugg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
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 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, _), .. }, ..},
impl LateLintPass<'_> for UnnecessarySortBy {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
match detect_lint(cx, expr) {
- Some(LintTrigger::SortByKey(trigger)) => utils::span_lint_and_sugg(
+ Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
cx,
UNNECESSARY_SORT_BY,
expr.span,
Applicability::MachineApplicable
},
),
- Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg(
+ Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
cx,
UNNECESSARY_SORT_BY,
expr.span,
-use crate::utils::{
- contains_return, in_macro, match_qpath, paths, return_ty, snippet, span_lint_and_then,
- visitors::find_all_ret_expressions,
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::{contains_return, in_macro, match_qpath, paths, return_ty, visitors::find_all_ret_expressions};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
}
},
FnKind::Closure => return,
- _ => (),
+ FnKind::Method(..) => (),
}
// Abort if the method is implementing a trait or of it a trait method.
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
-use crate::utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path};
-use crate::utils::{over, span_lint_and_then};
+use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::over;
use rustc_ast::mut_visit::*;
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
use rustc_ast::ast::{Item, ItemKind, UseTree, UseTreeKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{is_try, match_trait_method, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{is_try, match_trait_method, paths};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::visitors::LocalUsedVisitor;
use if_chain::if_chain;
use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::span_lint_and_help;
-use crate::utils::visitors::LocalUsedVisitor;
-
declare_clippy_lint! {
/// **What it does:** Checks methods that contain a `self` argument but don't use it
///
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::position_before_rarrow;
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_ast::visit::FnKind;
use rustc_span::source_map::Span;
use rustc_span::BytePos;
-use crate::utils::{position_before_rarrow, span_lint_and_sugg};
-
declare_clippy_lint! {
/// **What it does:** Checks for unit (`()`) expressions that can be removed.
///
-use crate::utils::{
- differing_macro_contexts, is_type_diagnostic_item, span_lint_and_then, usage::is_potentially_mutated,
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{differing_macro_contexts, usage::is_potentially_mutated};
use if_chain::if_chain;
use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnOp};
-use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{method_chain_args, return_ty};
use if_chain::if_chain;
use rustc_hir as hir;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::{Expr, ImplItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_middle::ty;
}
}
-use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
-use rustc_hir::{Expr, ImplItemKind};
-
struct FindExpectUnwrap<'a, 'tcx> {
lcx: &'a LateContext<'tcx>,
typeck_results: &'tcx ty::TypeckResults<'tcx>,
-use crate::utils::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
use if_chain::if_chain;
use itertools::Itertools;
-use rustc_ast::ast::{Item, ItemKind, Variant};
+use rustc_ast::ast::{Item, ItemKind, Variant, VisibilityKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
// assume that two-letter words are some kind of valid abbreviation like FP for false positive
// (and don't warn)
if (ident.chars().all(|c| c.is_ascii_uppercase()) && ident.len() > 2)
- // otherwise, warn if we have SOmeTHING lIKE THIs but only warn with the aggressive
+ // otherwise, warn if we have SOmeTHING lIKE THIs but only warn with the aggressive
// upper-case-acronyms-aggressive config option enabled
|| (be_aggressive && ident != &corrected)
{
it.kind,
ItemKind::TyAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Trait(..)
);
+ // do not lint public items
+ if !matches!(it.vis.kind, VisibilityKind::Public);
then {
check_ident(cx, &it.ident, self.upper_case_acronyms_aggressive);
}
-use crate::utils::{in_macro, meets_msrv, snippet_opt, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{in_macro, meets_msrv};
use if_chain::if_chain;
-
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
-use crate::utils::sugg::Sugg;
-use crate::utils::{
- get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet,
- snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::{snippet, snippet_with_macro_callsite};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{get_parent_expr, match_def_path, match_trait_method, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, HirId, MatchSource};
//! A group of attributes that can be attached to Rust code in order
//! to generate a clippy lint detecting said code automatically.
-use crate::utils::get_attr;
+use clippy_utils::get_attr;
use rustc_ast::ast::{LitFloatType, LitKind};
use rustc_ast::walk_list;
use rustc_data_structures::fx::FxHashMap;
//! checks for attributes
-use crate::utils::get_attr;
+use clippy_utils::get_attr;
use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use crate::consts::{constant_simple, Constant};
-use crate::utils::{
- is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, run_lints, snippet,
- span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
-};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::snippet;
+use clippy_utils::ty::match_type;
+use clippy_utils::{is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
use rustc_ast::visit::FnKind;
pub mod inspector;
#[cfg(feature = "internal-lints")]
pub mod internal_lints;
-
-pub use clippy_utils::*;
use crate::consts::{constant, Constant};
use crate::rustc_target::abi::LayoutOf;
-use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_copy;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind};
-use crate::utils::{
- is_type_diagnostic_item, match_def_path, path_to_local, path_to_local_id, paths, snippet, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{match_def_path, path_to_local, path_to_local_id, paths};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use crate::utils::span_lint_and_then;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
+use rustc_ast::LitKind;
use rustc_errors::Applicability;
+use rustc_hir as hir;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
-use crate::utils::{match_def_path, paths};
-use rustc_ast::LitKind;
-use rustc_hir as hir;
-
declare_clippy_lint! {
/// **What it does:** Finds occurrences of `Vec::resize(0, an_int)`
///
-use crate::utils::{match_type, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::paths;
+use clippy_utils::ty::match_type;
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
-use crate::utils::{run_lints, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::run_lints;
use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::{snippet, snippet_with_applicability};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
use std::borrow::Cow;
-use std::ops::Range;
-
-use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then};
-use if_chain::if_chain;
-use rustc_ast::ast::{
- Expr, ExprKind, ImplKind, Item, ItemKind, LitKind, MacCall, StrLit, StrStyle,
-};
-use rustc_ast::token;
+use std::iter;
+use std::ops::{Deref, Range};
+
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, MacCall, Path, StrLit, StrStyle};
+use rustc_ast::token::{self, LitKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::Applicability;
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::kw;
-use rustc_span::{sym, BytePos, Span};
+use rustc_span::symbol::{kw, Symbol};
+use rustc_span::{sym, BytePos, Span, DUMMY_SP};
declare_clippy_lint! {
/// **What it does:** This lint warns when you use `println!("")` to
sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi)
}
+/// Stores a list of replacement spans for each argument, but only if all the replacements used an
+/// empty format string.
+#[derive(Default)]
+struct SimpleFormatArgs {
+ unnamed: Vec<Vec<Span>>,
+ named: Vec<(Symbol, Vec<Span>)>,
+}
+impl SimpleFormatArgs {
+ fn get_unnamed(&self) -> impl Iterator<Item = &[Span]> {
+ self.unnamed.iter().map(|x| match x.as_slice() {
+ // Ignore the dummy span added from out of order format arguments.
+ [DUMMY_SP] => &[],
+ x => x,
+ })
+ }
+
+ fn get_named(&self, n: &Path) -> &[Span] {
+ self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice())
+ }
+
+ fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) {
+ use rustc_parse_format::{
+ AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec,
+ };
+
+ const SIMPLE: FormatSpec<'_> = FormatSpec {
+ fill: None,
+ align: AlignUnknown,
+ flags: 0,
+ precision: CountImplied,
+ precision_span: None,
+ width: CountImplied,
+ width_span: None,
+ ty: "",
+ ty_span: None,
+ };
+
+ match arg.position {
+ ArgumentIs(n) | ArgumentImplicitlyIs(n) => {
+ if self.unnamed.len() <= n {
+ // Use a dummy span to mark all unseen arguments.
+ self.unnamed.resize_with(n, || vec![DUMMY_SP]);
+ if arg.format == SIMPLE {
+ self.unnamed.push(vec![span]);
+ } else {
+ self.unnamed.push(Vec::new());
+ }
+ } else {
+ let args = &mut self.unnamed[n];
+ match (args.as_mut_slice(), arg.format == SIMPLE) {
+ // A non-empty format string has been seen already.
+ ([], _) => (),
+ // Replace the dummy span, if it exists.
+ ([dummy @ DUMMY_SP], true) => *dummy = span,
+ ([_, ..], true) => args.push(span),
+ ([_, ..], false) => *args = Vec::new(),
+ }
+ }
+ },
+ ArgumentNamed(n) => {
+ if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) {
+ match x.1.as_slice() {
+ // A non-empty format string has been seen already.
+ [] => (),
+ [_, ..] if arg.format == SIMPLE => x.1.push(span),
+ [_, ..] => x.1 = Vec::new(),
+ }
+ } else if arg.format == SIMPLE {
+ self.named.push((n, vec![span]));
+ } else {
+ self.named.push((n, Vec::new()));
+ }
+ },
+ };
+ }
+}
+
impl Write {
+ /// Parses a format string into a collection of spans for each argument. This only keeps track
+ /// of empty format arguments. Will also lint usages of debug format strings outside of debug
+ /// impls.
+ fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option<SimpleFormatArgs> {
+ use rustc_parse_format::{ParseMode, Parser, Piece};
+
+ let str_sym = str_lit.symbol_unescaped.as_str();
+ let style = match str_lit.style {
+ StrStyle::Cooked => None,
+ StrStyle::Raw(n) => Some(n as usize),
+ };
+
+ let mut parser = Parser::new(&str_sym, style, snippet_opt(cx, str_lit.span), false, ParseMode::Format);
+ let mut args = SimpleFormatArgs::default();
+
+ while let Some(arg) = parser.next() {
+ let arg = match arg {
+ Piece::String(_) => continue,
+ Piece::NextArgument(arg) => arg,
+ };
+ let span = parser
+ .arg_places
+ .last()
+ .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(x));
+
+ if !self.in_debug_impl && arg.format.ty == "?" {
+ // FIXME: modify rustc's fmt string parser to give us the current span
+ span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
+ }
+
+ args.push(arg, span);
+ }
+
+ parser.errors.is_empty().then(move || args)
+ }
+
/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
/// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
/// the contents of the string, whether it's a raw string, and the span of the literal in the
/// (Some("string to write: {}"), Some(buf))
/// ```
#[allow(clippy::too_many_lines)]
- fn check_tts<'a>(
- &self,
- cx: &EarlyContext<'a>,
- tts: TokenStream,
- is_write: bool,
- ) -> (Option<StrLit>, Option<Expr>) {
- use rustc_parse_format::{
- AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied,
- FormatSpec, ParseMode, Parser, Piece,
- };
-
+ fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) {
let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None);
- let mut expr: Option<Expr> = None;
- if is_write {
- expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
- Ok(p) => Some(p.into_inner()),
- Err(_) => return (None, None),
- };
- // might be `writeln!(foo)`
- if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() {
- return (None, expr);
+ let expr = if is_write {
+ match parser
+ .parse_expr()
+ .map(rustc_ast::ptr::P::into_inner)
+ .map_err(|mut e| e.cancel())
+ {
+ // write!(e, ...)
+ Ok(p) if parser.eat(&token::Comma) => Some(p),
+ // write!(e) or error
+ e => return (None, e.ok()),
}
- }
+ } else {
+ None
+ };
let fmtstr = match parser.parse_str_lit() {
Ok(fmtstr) => fmtstr,
Err(_) => return (None, expr),
};
- let tmp = fmtstr.symbol.as_str();
- let mut args = vec![];
- let mut fmt_parser = Parser::new(&tmp, None, None, false, ParseMode::Format);
- while let Some(piece) = fmt_parser.next() {
- if !fmt_parser.errors.is_empty() {
- return (None, expr);
- }
- if let Piece::NextArgument(arg) = piece {
- if !self.in_debug_impl && arg.format.ty == "?" {
- // FIXME: modify rustc's fmt string parser to give us the current span
- span_lint(
- cx,
- USE_DEBUG,
- parser.prev_token.span,
- "use of `Debug`-based formatting",
- );
- }
- args.push(arg);
- }
- }
+
+ let args = match self.parse_fmt_string(cx, &fmtstr) {
+ Some(args) => args,
+ None => return (Some(fmtstr), expr),
+ };
+
let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
- let mut idx = 0;
+ let mut unnamed_args = args.get_unnamed();
loop {
- const SIMPLE: FormatSpec<'_> = FormatSpec {
- fill: None,
- align: AlignUnknown,
- flags: 0,
- precision: CountImplied,
- precision_span: None,
- width: CountImplied,
- width_span: None,
- ty: "",
- ty_span: None,
- };
if !parser.eat(&token::Comma) {
return (Some(fmtstr), expr);
}
+
+ let comma_span = parser.prev_token.span;
let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) {
expr
} else {
return (Some(fmtstr), None);
};
- match &token_expr.kind {
- ExprKind::Lit(lit)
- if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) =>
- {
- let mut all_simple = true;
- let mut seen = false;
- for arg in &args {
- match arg.position {
- ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
- if n == idx {
- all_simple &= arg.format == SIMPLE;
- seen = true;
- }
- }
- ArgumentNamed(_) => {}
- }
- }
- if all_simple && seen {
- span_lint(cx, lint, token_expr.span, "literal with an empty format string");
- }
- idx += 1;
- }
- ExprKind::Assign(lhs, rhs, _) => {
- if_chain! {
- if let ExprKind::Lit(ref lit) = rhs.kind;
- if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..));
- if let ExprKind::Path(_, p) = &lhs.kind;
- then {
- let mut all_simple = true;
- let mut seen = false;
- for arg in &args {
- match arg.position {
- ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
- ArgumentNamed(name) => {
- if *p == name {
- seen = true;
- all_simple &= arg.format == SIMPLE;
- }
- },
- }
- }
- if all_simple && seen {
- span_lint(cx, lint, rhs.span, "literal with an empty format string");
- }
- }
- }
+ let (fmt_spans, lit) = match &token_expr.kind {
+ ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit),
+ ExprKind::Assign(lhs, rhs, _) => match (&lhs.kind, &rhs.kind) {
+ (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
+ _ => continue,
+ },
+ _ => {
+ unnamed_args.next();
+ continue;
+ },
+ };
+
+ let replacement: String = match lit.token.kind {
+ LitKind::Integer | LitKind::Float | LitKind::Err => continue,
+ LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
+ lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+ },
+ LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
+ lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+ },
+ LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue,
+ LitKind::Byte | LitKind::Char => match &*lit.token.symbol.as_str() {
+ "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"",
+ "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue,
+ "\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\",
+ "\\'" => "'",
+ "{" => "{{",
+ "}" => "}}",
+ x if matches!(fmtstr.style, StrStyle::Raw(_)) && x.starts_with('\\') => continue,
+ x => x,
}
- _ => idx += 1,
+ .into(),
+ LitKind::Bool => lit.token.symbol.as_str().deref().into(),
+ };
+
+ if !fmt_spans.is_empty() {
+ span_lint_and_then(
+ cx,
+ lint,
+ token_expr.span,
+ "literal with an empty format string",
+ |diag| {
+ diag.multipart_suggestion(
+ "try this",
+ iter::once((comma_span.to(token_expr.span), String::new()))
+ .chain(fmt_spans.iter().cloned().zip(iter::repeat(replacement)))
+ .collect(),
+ Applicability::MachineApplicable,
+ );
+ },
+ );
}
}
}
cx,
PRINT_WITH_NEWLINE,
mac.span(),
- &format!(
- "using `{}!()` with a format string that ends in a single newline",
- name
- ),
+ &format!("using `{}!()` with a format string that ends in a single newline", name),
|err| {
err.multipart_suggestion(
&format!("use `{}!` instead", suggested),
- vec![
- (mac.path.span, suggested),
- (newline_span(&fmt_str), String::new()),
- ],
+ vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())],
Applicability::MachineApplicable,
);
},
use crate::consts::{constant_simple, Constant};
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
use if_chain::if_chain;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::paths;
+use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item, match_type};
use if_chain::if_chain;
use rustc_hir::{self as hir, HirId, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_target::abi::LayoutOf as _;
use rustc_typeck::hir_ty_to_ty;
-use crate::utils::{is_normalizable, is_type_diagnostic_item, match_type, paths, span_lint_and_help};
-
declare_clippy_lint! {
/// **What it does:** Checks for maps with zero-sized value types anywhere in the code.
///
[package]
name = "clippy_utils"
-version = "0.1.52"
+version = "0.1.53"
authors = ["The Rust Clippy Developers"]
edition = "2018"
publish = false
itertools = "0.9"
regex-syntax = "0.6"
serde = { version = "1.0", features = ["derive"] }
-smallvec = { version = "1", features = ["union"] }
-toml = "0.5.3"
unicode-normalization = "0.1"
rustc-semver="1.1.0"
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
(Struct(lse), Struct(rse)) => {
- eq_path(&lse.path, &rse.path) &&
- eq_struct_rest(&lse.rest, &rse.rest) &&
- unordered_over(&lse.fields, &rse.fields, |l, r| eq_field(l, r))
+ eq_path(&lse.path, &rse.path)
+ && eq_struct_rest(&lse.rest, &rse.rest)
+ && unordered_over(&lse.fields, &rse.fields, |l, r| eq_field(l, r))
},
_ => false,
}
}
pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool {
- eq_expr(&l.value, &r.value)
+ eq_expr(&l.value, &r.value)
}
pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) {
if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
- diag.help(&format!(
- "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
- &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
- // extract just major + minor version and ignore patch versions
- format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap())
- }),
- lint.name_lower().replacen("clippy::", "", 1)
- ));
+ if let Some(lint) = lint.name_lower().strip_prefix("clippy::") {
+ diag.help(&format!(
+ "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
+ &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
+ // extract just major + minor version and ignore patch versions
+ format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap())
+ }),
+ lint
+ ));
+ }
}
}
//! - or-fun-call
//! - option-if-let-else
-use crate::{is_ctor_or_promotable_const_function, is_type_diagnostic_item};
+use crate::is_ctor_or_promotable_const_function;
+use crate::ty::is_type_diagnostic_item;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit;
use crate::consts::{constant_context, constant_simple};
-use crate::{differing_macro_contexts, snippet_opt};
+use crate::differing_macro_contexts;
+use crate::source::snippet_opt;
use rustc_ast::ast::InlineAsmTemplatePiece;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::def::Res;
use rustc_hir::{
- BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, ExprField, PatField, FnRetTy,
- GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path,
+ BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprField, ExprKind, FnRetTy, GenericArg,
+ GenericArgs, Guard, HirId, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path,
PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
};
use rustc_lexer::{tokenize, TokenKind};
pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
self.inter_expr().eq_path_segments(left, right)
}
-
- pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool {
- self.inter_expr().eq_ty_kind(left, right)
- }
}
struct HirEqInterExpr<'a, 'b, 'tcx> {
(&ExprKind::Struct(ref l_path, ref lf, ref lo), &ExprKind::Struct(ref r_path, ref rf, ref ro)) => {
self.eq_qpath(l_path, r_path)
&& both(lo, ro, |l, r| self.eq_expr(l, r))
- && over(lf, rf, |l, r| self.eq_field(l, r))
+ && over(lf, rf, |l, r| self.eq_expr_field(l, r))
},
(&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
(&ExprKind::Unary(l_op, ref le), &ExprKind::Unary(r_op, ref re)) => l_op == r_op && self.eq_expr(le, re),
over(left, right, |l, r| self.eq_expr(l, r))
}
- fn eq_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool {
+ fn eq_expr_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool {
left.ident.name == right.ident.name && self.eq_expr(&left.expr, &right.expr)
}
left.name == right.name
}
- fn eq_fieldpat(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool {
+ fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool {
let (PatField { ident: li, pat: lp, .. }, PatField { ident: ri, pat: rp, .. }) = (&left, &right);
li.name == ri.name && self.eq_pat(lp, rp)
}
match (&left.kind, &right.kind) {
(&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r),
(&PatKind::Struct(ref lp, ref la, ..), &PatKind::Struct(ref rp, ref ra, ..)) => {
- self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_fieldpat(l, r))
+ self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r))
},
(&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => {
self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
}
- fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
- self.eq_ty_kind(&left.kind, &right.kind)
- }
-
#[allow(clippy::similar_names)]
- fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool {
- match (left, right) {
+ fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
+ match (&left.kind, &right.kind) {
(&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec),
(&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => {
let cx = self.inner.cx;
pub mod camel_case;
pub mod comparisons;
pub mod consts;
-mod diagnostics;
+pub mod diagnostics;
pub mod eager_or_lazy;
pub mod higher;
mod hir_utils;
pub mod paths;
pub mod ptr;
pub mod qualify_min_const_fn;
+pub mod source;
pub mod sugg;
+pub mod ty;
pub mod usage;
pub mod visitors;
pub use self::attrs::*;
-pub use self::diagnostics::*;
pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
-use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::hash::BuildHasherDefault;
use if_chain::if_chain;
-use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind, Mutability};
+use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind};
use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
-use rustc_hir::Node;
use rustc_hir::{
- def, Arm, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, Item,
- ItemKind, LangItem, MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef,
- TyKind, Unsafety,
+ def, Arm, BindingAnnotation, Block, Body, Constness, CrateItem, Expr, ExprKind, FieldDef, FnDecl, ForeignItem,
+ GenericArgs, GenericParam, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, Local,
+ MacroDef, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, Stmt, TraitItem, TraitItemKind,
+ TraitRef, TyKind, Variant, Visibility,
};
-use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::exports::Export;
use rustc_middle::hir::map::Map;
-use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, layout::IntegerExt, DefIdTree, IntTy, Ty, TyCtxt, TypeFoldable, UintTy};
+use rustc_middle::ty as rustc_ty;
+use rustc_middle::ty::{layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable};
use rustc_semver::RustcVersion;
use rustc_session::Session;
-use rustc_span::hygiene::{self, ExpnKind, MacroKind};
+use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::original_sp;
use rustc_span::sym;
-use rustc_span::symbol::{kw, Symbol};
-use rustc_span::{BytePos, Pos, Span, SyntaxContext, DUMMY_SP};
+use rustc_span::symbol::{kw, Ident, Symbol};
+use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::Integer;
-use rustc_trait_selection::traits::query::normalize::AtExt;
-use smallvec::SmallVec;
use crate::consts::{constant, Constant};
-use std::collections::HashMap;
+use crate::ty::is_recursively_primitive_type;
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
if let Ok(version) = RustcVersion::parse(msrv) {
rhs.ctxt() != lhs.ctxt()
}
+/// If the given expression is a local binding, find the initializer expression.
+/// If that initializer expression is another local binding, find its initializer again.
+/// This process repeats as long as possible (but usually no more than once). Initializer
+/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
+/// instead.
+///
+/// Examples:
+/// ```ignore
+/// let abc = 1;
+/// // ^ output
+/// let def = abc;
+/// dbg!(def)
+/// // ^^^ input
+///
+/// // or...
+/// let abc = 1;
+/// let def = abc + 2;
+/// // ^^^^^^^ output
+/// dbg!(def)
+/// // ^^^ input
+/// ```
+pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
+ while let Some(init) = path_to_local(expr)
+ .and_then(|id| find_binding_init(cx, id))
+ .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
+ {
+ expr = init;
+ }
+ expr
+}
+
+/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
+/// By only considering immutable bindings, we guarantee that the returned expression represents the
+/// value of the binding wherever it is referenced.
+///
+/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
+/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
+/// canonical binding `HirId`.
+pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
+ let hir = cx.tcx.hir();
+ if_chain! {
+ if let Some(Node::Binding(pat)) = hir.find(hir_id);
+ if matches!(pat.kind, PatKind::Binding(BindingAnnotation::Unannotated, ..));
+ let parent = hir.get_parent_node(hir_id);
+ if let Some(Node::Local(local)) = hir.find(parent);
+ then {
+ return local.init;
+ }
+ }
+ None
+}
+
/// Returns `true` if the given `NodeId` is inside a constant context
///
/// # Example
}
}
-// If the snippet is empty, it's an attribute that was inserted during macro
-// expansion and we want to ignore those, because they could come from external
-// sources that the user has no control over.
-// For some reason these attributes don't have any expansion info on them, so
-// we have to check it this way until there is a better way.
-pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
- if let Some(snippet) = snippet_opt(cx, span) {
- if snippet.is_empty() {
- return false;
- }
- }
- true
-}
-
/// Checks if given pattern is a wildcard (`_`)
pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
matches!(pat.kind, PatKind::Wild)
}
-/// Checks if type is struct, enum or union type with the given def path.
-///
-/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
-/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
-pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
- match ty.kind() {
- ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
- _ => false,
- }
-}
-
-/// Checks if the type is equal to a diagnostic item
-///
-/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
-pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
- match ty.kind() {
- ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
- _ => false,
- }
-}
-
-/// Checks if the type is equal to a lang item
-pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
- match ty.kind() {
- ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did,
- _ => false,
- }
-}
-
/// Checks if the first type parameter is a lang item.
pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: LangItem) -> Option<&'tcx hir::Ty<'tcx>> {
let ty = get_qpath_generic_tys(qpath).next()?;
}
}
-/// Return `true` if the passed `typ` is `isize` or `usize`.
-pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
- matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
-}
-
/// Checks if the method call given in `expr` belongs to the given trait.
+/// This is a deprecated function, consider using [`is_trait_method`].
pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
let trt_id = cx.tcx.trait_of_item(def_id);
trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
}
-/// Checks if the method call given in `expr` belongs to a trait or other container with a given
+/// Checks if the method call given in `def_id` belongs to a trait or other container with a given
/// diagnostic item
pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
cx.tcx
.opt_associated_item(def_id)
.and_then(|associated_item| match associated_item.container {
- ty::TraitContainer(assoc_def_id) => Some(assoc_def_id),
- ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() {
- ty::Adt(adt, _) => Some(adt.did),
- ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works
+ rustc_ty::TraitContainer(assoc_def_id) => Some(assoc_def_id),
+ rustc_ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() {
+ rustc_ty::Adt(adt, _) => Some(adt.did),
+ rustc_ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works
_ => None,
},
})
.map_or(false, |assoc_def_id| cx.tcx.is_diagnostic_item(diag_item, assoc_def_id))
}
+/// Checks if the method call given in `expr` belongs to the given trait.
+pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
+ cx.typeck_results()
+ .type_dependent_def_id(expr.hir_id)
+ .map_or(false, |did| is_diagnostic_assoc_item(cx, did, diag_item))
+}
+
/// Checks if an expression references a variable of the given name.
pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
}
}
-/// Checks whether a type implements a trait.
-/// See also `get_trait_def_id`.
-pub fn implements_trait<'tcx>(
- cx: &LateContext<'tcx>,
- ty: Ty<'tcx>,
- trait_id: DefId,
- ty_params: &[GenericArg<'tcx>],
-) -> bool {
- // Do not check on infer_types to avoid panic in evaluate_obligation.
- if ty.has_infer_types() {
- return false;
- }
- let ty = cx.tcx.erase_regions(ty);
- if ty.has_escaping_bound_vars() {
- return false;
- }
- let ty_params = cx.tcx.mk_substs(ty_params.iter());
- cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env))
-}
-
/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
///
/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
None
}
-/// Checks whether this type implements `Drop`.
-pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
- match ty.ty_adt_def() {
- Some(def) => def.has_dtor(cx.tcx),
- None => false,
- }
-}
-
-/// Checks whether a type can be partially moved.
-pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
- if has_drop(cx, ty) || is_copy(cx, ty) {
- return false;
- }
- match ty.kind() {
- ty::Param(_) => false,
- ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
- _ => true,
- }
-}
-
/// Returns the method names and argument list of nested method call expressions that make up
/// `expr`. method/span lists are sorted with the most recent call first.
pub fn method_calls<'tcx>(
fmc.result
}
-/// Converts a span to a code snippet if available, otherwise use default.
-///
-/// This is useful if you want to provide suggestions for your lint or more generally, if you want
-/// to convert a given `Span` to a `str`.
-///
-/// # Example
-/// ```rust,ignore
-/// snippet(cx, expr.span, "..")
-/// ```
-pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
- snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
-}
-
-/// Same as `snippet`, but it adapts the applicability level by following rules:
-///
-/// - Applicability level `Unspecified` will never be changed.
-/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
-/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
-/// `HasPlaceholders`
-pub fn snippet_with_applicability<'a, T: LintContext>(
- cx: &T,
- span: Span,
- default: &'a str,
- applicability: &mut Applicability,
-) -> Cow<'a, str> {
- if *applicability != Applicability::Unspecified && span.from_expansion() {
- *applicability = Applicability::MaybeIncorrect;
- }
- snippet_opt(cx, span).map_or_else(
- || {
- if *applicability == Applicability::MachineApplicable {
- *applicability = Applicability::HasPlaceholders;
- }
- Cow::Borrowed(default)
- },
- From::from,
- )
-}
-
-/// Same as `snippet`, but should only be used when it's clear that the input span is
-/// not a macro argument.
-pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
- snippet(cx, span.source_callsite(), default)
-}
-
-/// Converts a span to a code snippet. Returns `None` if not available.
-pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
- cx.sess().source_map().span_to_snippet(span).ok()
-}
-
-/// Converts a span (from a block) to a code snippet if available, otherwise use default.
-///
-/// This trims the code of indentation, except for the first line. Use it for blocks or block-like
-/// things which need to be printed as such.
-///
-/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the
-/// resulting snippet of the given span.
-///
-/// # Example
-///
-/// ```rust,ignore
-/// snippet_block(cx, block.span, "..", None)
-/// // where, `block` is the block of the if expr
-/// if x {
-/// y;
-/// }
-/// // will return the snippet
-/// {
-/// y;
-/// }
-/// ```
-///
-/// ```rust,ignore
-/// snippet_block(cx, block.span, "..", Some(if_expr.span))
-/// // where, `block` is the block of the if expr
-/// if x {
-/// y;
-/// }
-/// // will return the snippet
-/// {
-/// y;
-/// } // aligned with `if`
-/// ```
-/// Note that the first line of the snippet always has 0 indentation.
-pub fn snippet_block<'a, T: LintContext>(
- cx: &T,
- span: Span,
- default: &'a str,
- indent_relative_to: Option<Span>,
-) -> Cow<'a, str> {
- let snip = snippet(cx, span, default);
- let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
- reindent_multiline(snip, true, indent)
-}
-
-/// Same as `snippet_block`, but adapts the applicability level by the rules of
-/// `snippet_with_applicability`.
-pub fn snippet_block_with_applicability<'a, T: LintContext>(
- cx: &T,
- span: Span,
- default: &'a str,
- indent_relative_to: Option<Span>,
- applicability: &mut Applicability,
-) -> Cow<'a, str> {
- let snip = snippet_with_applicability(cx, span, default, applicability);
- let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
- reindent_multiline(snip, true, indent)
-}
-
-/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
-/// will result in the macro call, rather then the expansion, if the span is from a child context.
-/// If the span is not from a child context, it will be used directly instead.
-///
-/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
-/// would result in `box []`. If given the context of the address of expression, this function will
-/// correctly get a snippet of `vec![]`.
-pub fn snippet_with_context(
- cx: &LateContext<'_>,
- span: Span,
- outer: SyntaxContext,
- default: &'a str,
- applicability: &mut Applicability,
-) -> Cow<'a, str> {
- let outer_span = hygiene::walk_chain(span, outer);
- let span = if outer_span.ctxt() == outer {
- outer_span
- } else {
- // The span is from a macro argument, and the outer context is the macro using the argument
- if *applicability != Applicability::Unspecified {
- *applicability = Applicability::MaybeIncorrect;
- }
- // TODO: get the argument span.
- span
- };
-
- snippet_with_applicability(cx, span, default, applicability)
-}
-
-/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
-/// line.
-///
-/// ```rust,ignore
-/// let x = ();
-/// // ^^
-/// // will be converted to
-/// let x = ();
-/// // ^^^^^^^^^^
-/// ```
-pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
- first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
-}
-
-fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
- let line_span = line_span(cx, span);
- snippet_opt(cx, line_span).and_then(|snip| {
- snip.find(|c: char| !c.is_whitespace())
- .map(|pos| line_span.lo() + BytePos::from_usize(pos))
- })
-}
-
-/// Returns the indentation of the line of a span
-///
-/// ```rust,ignore
-/// let x = ();
-/// // ^^ -- will return 0
-/// let x = ();
-/// // ^^ -- will return 4
-/// ```
-pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
- snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
-}
-
-/// Returns the positon just before rarrow
-///
-/// ```rust,ignore
-/// fn into(self) -> () {}
-/// ^
-/// // in case of unformatted code
-/// fn into2(self)-> () {}
-/// ^
-/// fn into3(self) -> () {}
-/// ^
-/// ```
-pub fn position_before_rarrow(s: &str) -> Option<usize> {
- s.rfind("->").map(|rpos| {
- let mut rpos = rpos;
- let chars: Vec<char> = s.chars().collect();
- while rpos > 1 {
- if let Some(c) = chars.get(rpos - 1) {
- if c.is_whitespace() {
- rpos -= 1;
- continue;
- }
- }
- break;
- }
- rpos
- })
-}
-
/// Extends the span to the beginning of the spans line, incl. whitespaces.
///
/// ```rust,ignore
Span::new(line_start, span.hi(), span.ctxt())
}
-/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
-/// Also takes an `Option<String>` which can be put inside the braces.
-pub fn expr_block<'a, T: LintContext>(
- cx: &T,
- expr: &Expr<'_>,
- option: Option<String>,
- default: &'a str,
- indent_relative_to: Option<Span>,
-) -> Cow<'a, str> {
- let code = snippet_block(cx, expr.span, default, indent_relative_to);
- let string = option.unwrap_or_default();
- if expr.span.from_expansion() {
- Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
- } else if let ExprKind::Block(_, _) = expr.kind {
- Cow::Owned(format!("{}{}", code, string))
- } else if string.is_empty() {
- Cow::Owned(format!("{{ {} }}", code))
- } else {
- Cow::Owned(format!("{{\n{};\n{}\n}}", code, string))
+/// Gets the span of the node, if there is one.
+pub fn get_node_span(node: Node<'_>) -> Option<Span> {
+ match node {
+ Node::Param(Param { span, .. })
+ | Node::Item(Item { span, .. })
+ | Node::ForeignItem(ForeignItem { span, .. })
+ | Node::TraitItem(TraitItem { span, .. })
+ | Node::ImplItem(ImplItem { span, .. })
+ | Node::Variant(Variant { span, .. })
+ | Node::Field(FieldDef { span, .. })
+ | Node::Expr(Expr { span, .. })
+ | Node::Stmt(Stmt { span, .. })
+ | Node::PathSegment(PathSegment {
+ ident: Ident { span, .. },
+ ..
+ })
+ | Node::Ty(hir::Ty { span, .. })
+ | Node::TraitRef(TraitRef {
+ path: Path { span, .. },
+ ..
+ })
+ | Node::Binding(Pat { span, .. })
+ | Node::Pat(Pat { span, .. })
+ | Node::Arm(Arm { span, .. })
+ | Node::Block(Block { span, .. })
+ | Node::Local(Local { span, .. })
+ | Node::MacroDef(MacroDef { span, .. })
+ | Node::Lifetime(Lifetime { span, .. })
+ | Node::GenericParam(GenericParam { span, .. })
+ | Node::Visibility(Visibility { span, .. })
+ | Node::Crate(CrateItem { span, .. }) => Some(*span),
+ Node::Ctor(_) | Node::AnonConst(_) => None,
}
}
-/// Reindent a multiline string with possibility of ignoring the first line.
-#[allow(clippy::needless_pass_by_value)]
-pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
- let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' ');
- let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t');
- reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into()
-}
-
-fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
- let x = s
- .lines()
- .skip(ignore_first as usize)
- .filter_map(|l| {
- if l.is_empty() {
- None
- } else {
- // ignore empty lines
- Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0)
- }
- })
- .min()
- .unwrap_or(0);
- let indent = indent.unwrap_or(0);
- s.lines()
- .enumerate()
- .map(|(i, l)| {
- if (ignore_first && i == 0) || l.is_empty() {
- l.to_owned()
- } else if x > indent {
- l.split_at(x - indent).1.to_owned()
- } else {
- " ".repeat(indent - x) + l
- }
- })
- .collect::<Vec<String>>()
- .join("\n")
+/// Gets the parent node, if any.
+pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
+ tcx.hir().parent_iter(id).next().map(|(_, node)| node)
}
/// Gets the parent expression, if any –- this is useful to constrain a lint.
pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
- let map = &cx.tcx.hir();
- let hir_id = e.hir_id;
- let parent_id = map.get_parent_node(hir_id);
- if hir_id == parent_id {
- return None;
+ match get_parent_node(cx.tcx, e.hir_id) {
+ Some(Node::Expr(parent)) => Some(parent),
+ _ => None,
}
- map.find(parent_id).and_then(|node| {
- if let Node::Expr(parent) = node {
- Some(parent)
- } else {
- None
- }
- })
}
pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
}
}
-/// Returns the base type for HIR references and pointers.
-pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
- match ty.kind {
- TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_ty.ty),
- _ => ty,
- }
-}
-
-/// Returns the base type for references and raw pointers, and count reference
-/// depth.
-pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
- fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
- match ty.kind() {
- ty::Ref(_, ty, _) => inner(ty, depth + 1),
- _ => (ty, depth),
- }
+/// Checks if the given expression is the else clause in the expression `if let .. {} else {}`
+pub fn is_else_clause_of_if_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+ let map = tcx.hir();
+ let mut iter = map.parent_iter(expr.hir_id);
+ let arm_id = match iter.next() {
+ Some((id, Node::Arm(..))) => id,
+ _ => return false,
+ };
+ match iter.next() {
+ Some((
+ _,
+ Node::Expr(Expr {
+ kind: ExprKind::Match(_, [_, else_arm], kind),
+ ..
+ }),
+ )) => else_arm.hir_id == arm_id && matches!(kind, MatchSource::IfLetDesugar { .. }),
+ _ => false,
}
- inner(ty, 0)
}
/// Checks whether the given expression is a constant integer of the given value.
cx.tcx.erase_late_bound_regions(ret_ty)
}
-/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
-pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool {
- ty.walk().any(|inner| match inner.unpack() {
- GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty),
- GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
- })
-}
-
-/// Returns `true` if the given type is an `unsafe` function.
-pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
- match ty.kind() {
- ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
- _ => false,
- }
-}
-
-pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
- ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env)
-}
-
/// Checks if an expression is constructing a tuple-like enum variant or struct
pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let ExprKind::Call(ref fun, _) = expr.kind {
},
PatKind::Slice(ref head, ref middle, ref tail) => {
match &cx.typeck_results().node_type(pat.hir_id).kind() {
- ty::Slice(..) => {
+ rustc_ty::Slice(..) => {
// [..] is the only irrefutable slice pattern.
!head.is_empty() || middle.is_none() || !tail.is_empty()
},
- ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)),
+ rustc_ty::Array(..) => {
+ are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat))
+ },
_ => {
// unreachable!()
true
}
}
+/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
+/// the function once on the given pattern.
+pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
+ if let PatKind::Or(pats) = pat.kind {
+ pats.iter().cloned().for_each(f)
+ } else {
+ f(pat)
+ }
+}
+
/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
/// implementations have.
pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
pat
}
-pub fn int_bits(tcx: TyCtxt<'_>, ity: ty::IntTy) -> u64 {
+pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
Integer::from_int_ty(&tcx, ity).size().bits()
}
#[allow(clippy::cast_possible_wrap)]
/// Turn a constant int byte representation into an i128
-pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: ty::IntTy) -> i128 {
+pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
let amt = 128 - int_bits(tcx, ity);
((u as i128) << amt) >> amt
}
#[allow(clippy::cast_sign_loss)]
/// clip unused bytes
-pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: ty::IntTy) -> u128 {
+pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
let amt = 128 - int_bits(tcx, ity);
((u as u128) << amt) >> amt
}
/// clip unused bytes
-pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: ty::UintTy) -> u128 {
+pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
let amt = 128 - bits;
(u << amt) >> amt
}
-/// Removes block comments from the given `Vec` of lines.
-///
-/// # Examples
-///
-/// ```rust,ignore
-/// without_block_comments(vec!["/*", "foo", "*/"]);
-/// // => vec![]
-///
-/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
-/// // => vec!["bar"]
-/// ```
-pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
- let mut without = vec![];
-
- let mut nest_level = 0;
-
- for line in lines {
- if line.contains("/*") {
- nest_level += 1;
- continue;
- } else if line.contains("*/") {
- nest_level -= 1;
- continue;
- }
-
- if nest_level == 0 {
- without.push(line);
- }
- }
-
- without
-}
-
pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
let map = &tcx.hir();
let mut prev_enclosing_node = None;
false
}
-/// Returns true if ty has `iter` or `iter_mut` methods
-pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
- // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
- // exists and has the desired signature. Unfortunately FnCtxt is not exported
- // so we can't use its `lookup_method` method.
- let into_iter_collections: &[Symbol] = &[
- sym::vec_type,
- sym::option_type,
- sym::result_type,
- sym::BTreeMap,
- sym::BTreeSet,
- sym::vecdeque_type,
- sym::LinkedList,
- sym::BinaryHeap,
- sym::hashset_type,
- sym::hashmap_type,
- sym::PathBuf,
- sym::Path,
- sym::Receiver,
- ];
-
- let ty_to_check = match probably_ref_ty.kind() {
- ty::Ref(_, ty_to_check, _) => ty_to_check,
- _ => probably_ref_ty,
- };
-
- let def_id = match ty_to_check.kind() {
- ty::Array(..) => return Some(sym::array),
- ty::Slice(..) => return Some(sym::slice),
- ty::Adt(adt, _) => adt.did,
- _ => return None,
- };
-
- for &name in into_iter_collections {
- if cx.tcx.is_diagnostic_item(name, def_id) {
- return Some(cx.tcx.item_name(def_id));
- }
- }
- None
-}
-
/// Matches a function call with the given path and returns the arguments.
///
/// Usage:
None
}
-// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
-// this function can be removed once the `normalizie` method does not panic when normalization does
-// not succeed
-/// Checks if `Ty` is normalizable. This function is useful
-/// to avoid crashes on `layout_of`.
-pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
- is_normalizable_helper(cx, param_env, ty, &mut HashMap::new())
-}
-
-fn is_normalizable_helper<'tcx>(
- cx: &LateContext<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ty: Ty<'tcx>,
- cache: &mut HashMap<Ty<'tcx>, bool>,
-) -> bool {
- if let Some(&cached_result) = cache.get(ty) {
- return cached_result;
- }
- // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
- cache.insert(ty, false);
- let result = cx.tcx.infer_ctxt().enter(|infcx| {
- let cause = rustc_middle::traits::ObligationCause::dummy();
- if infcx.at(&cause, param_env).normalize(ty).is_ok() {
- match ty.kind() {
- ty::Adt(def, substs) => def.variants.iter().all(|variant| {
- variant
- .fields
- .iter()
- .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
- }),
- _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
- GenericArgKind::Type(inner_ty) if inner_ty != ty => {
- is_normalizable_helper(cx, param_env, inner_ty, cache)
- },
- _ => true, // if inner_ty == ty, we've already checked it
- }),
- }
- } else {
- false
- }
- });
- cache.insert(ty, result);
- result
-}
-
pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
// We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path`
// accepts only that. We should probably move to Symbols in Clippy as well.
/// sequence of `if/else`.
/// E.g., this returns `([a, b], [c, d, e])` for the expression
/// `if a { c } else if b { d } else { e }`.
-pub fn if_sequence<'tcx>(
- mut expr: &'tcx Expr<'tcx>,
-) -> (SmallVec<[&'tcx Expr<'tcx>; 1]>, SmallVec<[&'tcx Block<'tcx>; 1]>) {
- let mut conds = SmallVec::new();
- let mut blocks: SmallVec<[&Block<'_>; 1]> = SmallVec::new();
+pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
+ let mut conds = Vec::new();
+ let mut blocks: Vec<&Block<'_>> = Vec::new();
while let ExprKind::If(ref cond, ref then_expr, ref else_expr) = expr.kind {
conds.push(&**cond);
attr_by_name(attrs, "must_use")
}
-// Returns whether the type has #[must_use] attribute
-pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
- match ty.kind() {
- ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(),
- ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(),
- ty::Slice(ref ty)
- | ty::Array(ref ty, _)
- | ty::RawPtr(ty::TypeAndMut { ref ty, .. })
- | ty::Ref(_, ref ty, _) => {
- // for the Array case we don't need to care for the len == 0 case
- // because we don't want to lint functions returning empty arrays
- is_must_use_ty(cx, *ty)
- },
- ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
- ty::Opaque(ref def_id, _) => {
- for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
- if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() {
- if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
- return true;
- }
- }
- }
- false
- },
- ty::Dynamic(binder, _) => {
- for predicate in binder.iter() {
- if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
- if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
- return true;
- }
- }
- }
- false
- },
- _ => false,
- }
-}
-
// check if expr is calling method or function with #[must_use] attribute
pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let did = match expr.kind {
})
}
-/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point
-/// number type, a str, or an array, slice, or tuple of those types).
-pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
- match ty.kind() {
- ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
- ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
- ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
- ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
- _ => false,
- }
-}
-
/// Returns Option<String> where String is a textual representation of the type encapsulated in the
/// slice iff the given expression is a slice of primitives (as defined in the
/// `is_recursively_primitive_type` function) and None otherwise.
let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
let expr_kind = expr_type.kind();
let is_primitive = match expr_kind {
- ty::Slice(element_type) => is_recursively_primitive_type(element_type),
- ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &ty::Slice(_)) => {
- if let ty::Slice(element_type) = inner_ty.kind() {
+ rustc_ty::Slice(element_type) => is_recursively_primitive_type(element_type),
+ rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
+ if let rustc_ty::Slice(element_type) = inner_ty.kind() {
is_recursively_primitive_type(element_type)
} else {
unreachable!()
// if we have wrappers like Array, Slice or Tuple, print these
// and get the type enclosed in the slice ref
match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
- ty::Slice(..) => return Some("slice".into()),
- ty::Array(..) => return Some("array".into()),
- ty::Tuple(..) => return Some("tuple".into()),
+ rustc_ty::Slice(..) => return Some("slice".into()),
+ rustc_ty::Array(..) => return Some("array".into()),
+ rustc_ty::Tuple(..) => return Some("tuple".into()),
_ => {
// is_recursively_primitive_type() should have taken care
// of the rest and we can rely on the type that is found
f(expr, 0)
}
-/// Peels off all references on the type. Returns the underlying type and the number of references
-/// removed.
-pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
- fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
- if let ty::Ref(_, ty, _) = ty.kind() {
- peel(ty, count + 1)
- } else {
- (ty, count)
- }
- }
- peel(ty, 0)
-}
-
-/// Peels off all references on the type.Returns the underlying type, the number of references
-/// removed, and whether the pointer is ultimately mutable or not.
-pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
- fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
- match ty.kind() {
- ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability),
- ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not),
- _ => (ty, count, mutability),
- }
- }
- f(ty, 0, Mutability::Mut)
-}
-
#[macro_export]
macro_rules! unwrap_cargo_metadata {
($cx: ident, $lint: ident, $deps: expr) => {{
}
false
}
-
-#[cfg(test)]
-mod test {
- use super::{reindent_multiline, without_block_comments};
-
- #[test]
- fn test_reindent_multiline_single_line() {
- assert_eq!("", reindent_multiline("".into(), false, None));
- assert_eq!("...", reindent_multiline("...".into(), false, None));
- assert_eq!("...", reindent_multiline(" ...".into(), false, None));
- assert_eq!("...", reindent_multiline("\t...".into(), false, None));
- assert_eq!("...", reindent_multiline("\t\t...".into(), false, None));
- }
-
- #[test]
- #[rustfmt::skip]
- fn test_reindent_multiline_block() {
- assert_eq!("\
- if x {
- y
- } else {
- z
- }", reindent_multiline(" if x {
- y
- } else {
- z
- }".into(), false, None));
- assert_eq!("\
- if x {
- \ty
- } else {
- \tz
- }", reindent_multiline(" if x {
- \ty
- } else {
- \tz
- }".into(), false, None));
- }
-
- #[test]
- #[rustfmt::skip]
- fn test_reindent_multiline_empty_line() {
- assert_eq!("\
- if x {
- y
-
- } else {
- z
- }", reindent_multiline(" if x {
- y
-
- } else {
- z
- }".into(), false, None));
- }
-
- #[test]
- #[rustfmt::skip]
- fn test_reindent_multiline_lines_deeper() {
- assert_eq!("\
- if x {
- y
- } else {
- z
- }", reindent_multiline("\
- if x {
- y
- } else {
- z
- }".into(), true, Some(8)));
- }
-
- #[test]
- fn test_without_block_comments_lines_without_block_comments() {
- let result = without_block_comments(vec!["/*", "", "*/"]);
- println!("result: {:?}", result);
- assert!(result.is_empty());
-
- let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
- assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
-
- let result = without_block_comments(vec!["/* rust", "", "*/"]);
- assert!(result.is_empty());
-
- let result = without_block_comments(vec!["/* one-line comment */"]);
- assert!(result.is_empty());
-
- let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
- assert!(result.is_empty());
-
- let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
- assert!(result.is_empty());
-
- let result = without_block_comments(vec!["foo", "bar", "baz"]);
- assert_eq!(result, vec!["foo", "bar", "baz"]);
- }
-}
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"];
pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"];
-pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
#[cfg(feature = "internal-lints")]
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
#[cfg(feature = "internal-lints")]
-use crate::{get_pat_name, match_var, snippet};
+use crate::source::snippet;
+use crate::{get_pat_name, match_var};
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Body, BodyId, Expr, ExprKind, Param};
use rustc_lint::LateContext;
--- /dev/null
+//! Utils for extracting, inspecting or transforming source code
+
+#![allow(clippy::module_name_repetitions)]
+
+use crate::line_span;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LintContext};
+use rustc_span::hygiene;
+use rustc_span::{BytePos, Pos, Span, SyntaxContext};
+use std::borrow::Cow;
+
+/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
+/// Also takes an `Option<String>` which can be put inside the braces.
+pub fn expr_block<'a, T: LintContext>(
+ cx: &T,
+ expr: &Expr<'_>,
+ option: Option<String>,
+ default: &'a str,
+ indent_relative_to: Option<Span>,
+) -> Cow<'a, str> {
+ let code = snippet_block(cx, expr.span, default, indent_relative_to);
+ let string = option.unwrap_or_default();
+ if expr.span.from_expansion() {
+ Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
+ } else if let ExprKind::Block(_, _) = expr.kind {
+ Cow::Owned(format!("{}{}", code, string))
+ } else if string.is_empty() {
+ Cow::Owned(format!("{{ {} }}", code))
+ } else {
+ Cow::Owned(format!("{{\n{};\n{}\n}}", code, string))
+ }
+}
+
+/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
+/// line.
+///
+/// ```rust,ignore
+/// let x = ();
+/// // ^^
+/// // will be converted to
+/// let x = ();
+/// // ^^^^^^^^^^
+/// ```
+pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
+ first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
+}
+
+fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
+ let line_span = line_span(cx, span);
+ snippet_opt(cx, line_span).and_then(|snip| {
+ snip.find(|c: char| !c.is_whitespace())
+ .map(|pos| line_span.lo() + BytePos::from_usize(pos))
+ })
+}
+
+/// Returns the indentation of the line of a span
+///
+/// ```rust,ignore
+/// let x = ();
+/// // ^^ -- will return 0
+/// let x = ();
+/// // ^^ -- will return 4
+/// ```
+pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
+ snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
+}
+
+// If the snippet is empty, it's an attribute that was inserted during macro
+// expansion and we want to ignore those, because they could come from external
+// sources that the user has no control over.
+// For some reason these attributes don't have any expansion info on them, so
+// we have to check it this way until there is a better way.
+pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
+ if let Some(snippet) = snippet_opt(cx, span) {
+ if snippet.is_empty() {
+ return false;
+ }
+ }
+ true
+}
+
+/// Returns the positon just before rarrow
+///
+/// ```rust,ignore
+/// fn into(self) -> () {}
+/// ^
+/// // in case of unformatted code
+/// fn into2(self)-> () {}
+/// ^
+/// fn into3(self) -> () {}
+/// ^
+/// ```
+pub fn position_before_rarrow(s: &str) -> Option<usize> {
+ s.rfind("->").map(|rpos| {
+ let mut rpos = rpos;
+ let chars: Vec<char> = s.chars().collect();
+ while rpos > 1 {
+ if let Some(c) = chars.get(rpos - 1) {
+ if c.is_whitespace() {
+ rpos -= 1;
+ continue;
+ }
+ }
+ break;
+ }
+ rpos
+ })
+}
+
+/// Reindent a multiline string with possibility of ignoring the first line.
+#[allow(clippy::needless_pass_by_value)]
+pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
+ let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' ');
+ let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t');
+ reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into()
+}
+
+fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
+ let x = s
+ .lines()
+ .skip(ignore_first as usize)
+ .filter_map(|l| {
+ if l.is_empty() {
+ None
+ } else {
+ // ignore empty lines
+ Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0)
+ }
+ })
+ .min()
+ .unwrap_or(0);
+ let indent = indent.unwrap_or(0);
+ s.lines()
+ .enumerate()
+ .map(|(i, l)| {
+ if (ignore_first && i == 0) || l.is_empty() {
+ l.to_owned()
+ } else if x > indent {
+ l.split_at(x - indent).1.to_owned()
+ } else {
+ " ".repeat(indent - x) + l
+ }
+ })
+ .collect::<Vec<String>>()
+ .join("\n")
+}
+
+/// Converts a span to a code snippet if available, otherwise use default.
+///
+/// This is useful if you want to provide suggestions for your lint or more generally, if you want
+/// to convert a given `Span` to a `str`.
+///
+/// # Example
+/// ```rust,ignore
+/// snippet(cx, expr.span, "..")
+/// ```
+pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
+ snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
+}
+
+/// Same as `snippet`, but it adapts the applicability level by following rules:
+///
+/// - Applicability level `Unspecified` will never be changed.
+/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
+/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
+/// `HasPlaceholders`
+pub fn snippet_with_applicability<'a, T: LintContext>(
+ cx: &T,
+ span: Span,
+ default: &'a str,
+ applicability: &mut Applicability,
+) -> Cow<'a, str> {
+ if *applicability != Applicability::Unspecified && span.from_expansion() {
+ *applicability = Applicability::MaybeIncorrect;
+ }
+ snippet_opt(cx, span).map_or_else(
+ || {
+ if *applicability == Applicability::MachineApplicable {
+ *applicability = Applicability::HasPlaceholders;
+ }
+ Cow::Borrowed(default)
+ },
+ From::from,
+ )
+}
+
+/// Same as `snippet`, but should only be used when it's clear that the input span is
+/// not a macro argument.
+pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
+ snippet(cx, span.source_callsite(), default)
+}
+
+/// Converts a span to a code snippet. Returns `None` if not available.
+pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
+ cx.sess().source_map().span_to_snippet(span).ok()
+}
+
+/// Converts a span (from a block) to a code snippet if available, otherwise use default.
+///
+/// This trims the code of indentation, except for the first line. Use it for blocks or block-like
+/// things which need to be printed as such.
+///
+/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the
+/// resulting snippet of the given span.
+///
+/// # Example
+///
+/// ```rust,ignore
+/// snippet_block(cx, block.span, "..", None)
+/// // where, `block` is the block of the if expr
+/// if x {
+/// y;
+/// }
+/// // will return the snippet
+/// {
+/// y;
+/// }
+/// ```
+///
+/// ```rust,ignore
+/// snippet_block(cx, block.span, "..", Some(if_expr.span))
+/// // where, `block` is the block of the if expr
+/// if x {
+/// y;
+/// }
+/// // will return the snippet
+/// {
+/// y;
+/// } // aligned with `if`
+/// ```
+/// Note that the first line of the snippet always has 0 indentation.
+pub fn snippet_block<'a, T: LintContext>(
+ cx: &T,
+ span: Span,
+ default: &'a str,
+ indent_relative_to: Option<Span>,
+) -> Cow<'a, str> {
+ let snip = snippet(cx, span, default);
+ let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
+ reindent_multiline(snip, true, indent)
+}
+
+/// Same as `snippet_block`, but adapts the applicability level by the rules of
+/// `snippet_with_applicability`.
+pub fn snippet_block_with_applicability<'a, T: LintContext>(
+ cx: &T,
+ span: Span,
+ default: &'a str,
+ indent_relative_to: Option<Span>,
+ applicability: &mut Applicability,
+) -> Cow<'a, str> {
+ let snip = snippet_with_applicability(cx, span, default, applicability);
+ let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
+ reindent_multiline(snip, true, indent)
+}
+
+/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
+/// will result in the macro call, rather then the expansion, if the span is from a child context.
+/// If the span is not from a child context, it will be used directly instead.
+///
+/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
+/// would result in `box []`. If given the context of the address of expression, this function will
+/// correctly get a snippet of `vec![]`.
+///
+/// This will also return whether or not the snippet is a macro call.
+pub fn snippet_with_context(
+ cx: &LateContext<'_>,
+ span: Span,
+ outer: SyntaxContext,
+ default: &'a str,
+ applicability: &mut Applicability,
+) -> (Cow<'a, str>, bool) {
+ let outer_span = hygiene::walk_chain(span, outer);
+ let (span, is_macro_call) = if outer_span.ctxt() == outer {
+ (outer_span, span.ctxt() != outer)
+ } else {
+ // The span is from a macro argument, and the outer context is the macro using the argument
+ if *applicability != Applicability::Unspecified {
+ *applicability = Applicability::MaybeIncorrect;
+ }
+ // TODO: get the argument span.
+ (span, false)
+ };
+
+ (
+ snippet_with_applicability(cx, span, default, applicability),
+ is_macro_call,
+ )
+}
+
+/// Removes block comments from the given `Vec` of lines.
+///
+/// # Examples
+///
+/// ```rust,ignore
+/// without_block_comments(vec!["/*", "foo", "*/"]);
+/// // => vec![]
+///
+/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
+/// // => vec!["bar"]
+/// ```
+pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
+ let mut without = vec![];
+
+ let mut nest_level = 0;
+
+ for line in lines {
+ if line.contains("/*") {
+ nest_level += 1;
+ continue;
+ } else if line.contains("*/") {
+ nest_level -= 1;
+ continue;
+ }
+
+ if nest_level == 0 {
+ without.push(line);
+ }
+ }
+
+ without
+}
+
+#[cfg(test)]
+mod test {
+ use super::{reindent_multiline, without_block_comments};
+
+ #[test]
+ fn test_reindent_multiline_single_line() {
+ assert_eq!("", reindent_multiline("".into(), false, None));
+ assert_eq!("...", reindent_multiline("...".into(), false, None));
+ assert_eq!("...", reindent_multiline(" ...".into(), false, None));
+ assert_eq!("...", reindent_multiline("\t...".into(), false, None));
+ assert_eq!("...", reindent_multiline("\t\t...".into(), false, None));
+ }
+
+ #[test]
+ #[rustfmt::skip]
+ fn test_reindent_multiline_block() {
+ assert_eq!("\
+ if x {
+ y
+ } else {
+ z
+ }", reindent_multiline(" if x {
+ y
+ } else {
+ z
+ }".into(), false, None));
+ assert_eq!("\
+ if x {
+ \ty
+ } else {
+ \tz
+ }", reindent_multiline(" if x {
+ \ty
+ } else {
+ \tz
+ }".into(), false, None));
+ }
+
+ #[test]
+ #[rustfmt::skip]
+ fn test_reindent_multiline_empty_line() {
+ assert_eq!("\
+ if x {
+ y
+
+ } else {
+ z
+ }", reindent_multiline(" if x {
+ y
+
+ } else {
+ z
+ }".into(), false, None));
+ }
+
+ #[test]
+ #[rustfmt::skip]
+ fn test_reindent_multiline_lines_deeper() {
+ assert_eq!("\
+ if x {
+ y
+ } else {
+ z
+ }", reindent_multiline("\
+ if x {
+ y
+ } else {
+ z
+ }".into(), true, Some(8)));
+ }
+
+ #[test]
+ fn test_without_block_comments_lines_without_block_comments() {
+ let result = without_block_comments(vec!["/*", "", "*/"]);
+ println!("result: {:?}", result);
+ assert!(result.is_empty());
+
+ let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
+ assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
+
+ let result = without_block_comments(vec!["/* rust", "", "*/"]);
+ assert!(result.is_empty());
+
+ let result = without_block_comments(vec!["/* one-line comment */"]);
+ assert!(result.is_empty());
+
+ let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
+ assert!(result.is_empty());
+
+ let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
+ assert!(result.is_empty());
+
+ let result = without_block_comments(vec!["foo", "bar", "baz"]);
+ assert_eq!(result, vec!["foo", "bar", "baz"]);
+ }
+}
//! Contains utility functions to generate suggestions.
#![deny(clippy::missing_docs_in_private_items)]
-use crate::{higher, snippet, snippet_opt, snippet_with_macro_callsite};
+use crate::higher;
+use crate::source::{snippet, snippet_opt, snippet_with_macro_callsite};
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{ast, token};
use rustc_ast_pretty::pprust::token_kind_to_string;
--- /dev/null
+//! Util methods for [`rustc_middle::ty`]
+
+#![allow(clippy::module_name_repetitions)]
+
+use std::collections::HashMap;
+
+use rustc_ast::ast::Mutability;
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{TyKind, Unsafety};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_lint::LateContext;
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
+use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy};
+use rustc_span::sym;
+use rustc_span::symbol::Symbol;
+use rustc_span::DUMMY_SP;
+use rustc_trait_selection::traits::query::normalize::AtExt;
+
+use crate::{match_def_path, must_use_attr};
+
+pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env)
+}
+
+/// Checks whether a type can be partially moved.
+pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ if has_drop(cx, ty) || is_copy(cx, ty) {
+ return false;
+ }
+ match ty.kind() {
+ ty::Param(_) => false,
+ ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
+ _ => true,
+ }
+}
+
+/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
+pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool {
+ ty.walk().any(|inner| match inner.unpack() {
+ GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty),
+ GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+ })
+}
+
+/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
+/// constructor.
+pub fn contains_adt_constructor(ty: Ty<'_>, adt: &AdtDef) -> bool {
+ ty.walk().any(|inner| match inner.unpack() {
+ GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt),
+ GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+ })
+}
+
+/// Returns true if ty has `iter` or `iter_mut` methods
+pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
+ // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
+ // exists and has the desired signature. Unfortunately FnCtxt is not exported
+ // so we can't use its `lookup_method` method.
+ let into_iter_collections: &[Symbol] = &[
+ sym::vec_type,
+ sym::option_type,
+ sym::result_type,
+ sym::BTreeMap,
+ sym::BTreeSet,
+ sym::vecdeque_type,
+ sym::LinkedList,
+ sym::BinaryHeap,
+ sym::hashset_type,
+ sym::hashmap_type,
+ sym::PathBuf,
+ sym::Path,
+ sym::Receiver,
+ ];
+
+ let ty_to_check = match probably_ref_ty.kind() {
+ ty::Ref(_, ty_to_check, _) => ty_to_check,
+ _ => probably_ref_ty,
+ };
+
+ let def_id = match ty_to_check.kind() {
+ ty::Array(..) => return Some(sym::array),
+ ty::Slice(..) => return Some(sym::slice),
+ ty::Adt(adt, _) => adt.did,
+ _ => return None,
+ };
+
+ for &name in into_iter_collections {
+ if cx.tcx.is_diagnostic_item(name, def_id) {
+ return Some(cx.tcx.item_name(def_id));
+ }
+ }
+ None
+}
+
+/// Checks whether a type implements a trait.
+/// See also `get_trait_def_id`.
+pub fn implements_trait<'tcx>(
+ cx: &LateContext<'tcx>,
+ ty: Ty<'tcx>,
+ trait_id: DefId,
+ ty_params: &[GenericArg<'tcx>],
+) -> bool {
+ // Do not check on infer_types to avoid panic in evaluate_obligation.
+ if ty.has_infer_types() {
+ return false;
+ }
+ let ty = cx.tcx.erase_regions(ty);
+ if ty.has_escaping_bound_vars() {
+ return false;
+ }
+ let ty_params = cx.tcx.mk_substs(ty_params.iter());
+ cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env))
+}
+
+/// Checks whether this type implements `Drop`.
+pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ match ty.ty_adt_def() {
+ Some(def) => def.has_dtor(cx.tcx),
+ None => false,
+ }
+}
+
+// Returns whether the type has #[must_use] attribute
+pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ match ty.kind() {
+ ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(),
+ ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(),
+ ty::Slice(ref ty)
+ | ty::Array(ref ty, _)
+ | ty::RawPtr(ty::TypeAndMut { ref ty, .. })
+ | ty::Ref(_, ref ty, _) => {
+ // for the Array case we don't need to care for the len == 0 case
+ // because we don't want to lint functions returning empty arrays
+ is_must_use_ty(cx, *ty)
+ },
+ ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
+ ty::Opaque(ref def_id, _) => {
+ for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
+ if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() {
+ if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
+ return true;
+ }
+ }
+ }
+ false
+ },
+ ty::Dynamic(binder, _) => {
+ for predicate in binder.iter() {
+ if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
+ if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
+ return true;
+ }
+ }
+ }
+ false
+ },
+ _ => false,
+ }
+}
+
+// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
+// this function can be removed once the `normalizie` method does not panic when normalization does
+// not succeed
+/// Checks if `Ty` is normalizable. This function is useful
+/// to avoid crashes on `layout_of`.
+pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
+ is_normalizable_helper(cx, param_env, ty, &mut HashMap::new())
+}
+
+fn is_normalizable_helper<'tcx>(
+ cx: &LateContext<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ty: Ty<'tcx>,
+ cache: &mut HashMap<Ty<'tcx>, bool>,
+) -> bool {
+ if let Some(&cached_result) = cache.get(ty) {
+ return cached_result;
+ }
+ // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
+ cache.insert(ty, false);
+ let result = cx.tcx.infer_ctxt().enter(|infcx| {
+ let cause = rustc_middle::traits::ObligationCause::dummy();
+ if infcx.at(&cause, param_env).normalize(ty).is_ok() {
+ match ty.kind() {
+ ty::Adt(def, substs) => def.variants.iter().all(|variant| {
+ variant
+ .fields
+ .iter()
+ .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
+ }),
+ _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
+ GenericArgKind::Type(inner_ty) if inner_ty != ty => {
+ is_normalizable_helper(cx, param_env, inner_ty, cache)
+ },
+ _ => true, // if inner_ty == ty, we've already checked it
+ }),
+ }
+ } else {
+ false
+ }
+ });
+ cache.insert(ty, result);
+ result
+}
+
+/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point
+/// number type, a str, or an array, slice, or tuple of those types).
+pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
+ match ty.kind() {
+ ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
+ ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
+ ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
+ ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
+ _ => false,
+ }
+}
+
+/// Checks if the type is equal to a diagnostic item
+///
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
+pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
+ match ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
+ _ => false,
+ }
+}
+
+/// Checks if the type is equal to a lang item
+pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
+ match ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did,
+ _ => false,
+ }
+}
+
+/// Return `true` if the passed `typ` is `isize` or `usize`.
+pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
+ matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
+}
+
+/// Checks if type is struct, enum or union type with the given def path.
+///
+/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
+pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
+ match ty.kind() {
+ ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
+ _ => false,
+ }
+}
+
+/// Peels off all references on the type. Returns the underlying type and the number of references
+/// removed.
+pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
+ fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
+ if let ty::Ref(_, ty, _) = ty.kind() {
+ peel(ty, count + 1)
+ } else {
+ (ty, count)
+ }
+ }
+ peel(ty, 0)
+}
+
+/// Peels off all references on the type.Returns the underlying type, the number of references
+/// removed, and whether the pointer is ultimately mutable or not.
+pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
+ fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
+ match ty.kind() {
+ ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability),
+ ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not),
+ _ => (ty, count, mutability),
+ }
+ }
+ f(ty, 0, Mutability::Mut)
+}
+
+/// Returns `true` if the given type is an `unsafe` function.
+pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ match ty.kind() {
+ ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
+ _ => false,
+ }
+}
+
+/// Returns the base type for HIR references and pointers.
+pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
+ match ty.kind {
+ TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_ty.ty),
+ _ => ty,
+ }
+}
+
+/// Returns the base type for references and raw pointers, and count reference
+/// depth.
+pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
+ fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
+ match ty.kind() {
+ ty::Ref(_, ty, _) => inner(ty, depth + 1),
+ _ => (ty, depth),
+ }
+ }
+ inner(ty, 0)
+}
use rustc_hir::{Expr, ExprKind, HirId, Path};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
-use rustc_middle::mir::FakeReadCause;
use rustc_middle::hir::map::Map;
+use rustc_middle::mir::FakeReadCause;
use rustc_middle::ty;
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
self.update(&cmt)
}
- fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _:HirId) { }
+ fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
}
pub struct ParamBindingIdCollector {
cargo dev update_lints
# create a new lint and register it
cargo dev new_lint
-# (experimental) Setup Clippy to work with rust-analyzer
-cargo dev ra_setup
+# (experimental) Setup Clippy to work with IntelliJ-Rust
+cargo dev ide_setup
```
## lintcheck
[toolchain]
-channel = "nightly-2021-03-11"
+channel = "nightly-2021-03-25"
components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
// `GccLlvmSomething`
struct GCCLLVMSomething;
+// don't warn on public items
+pub struct MIXEDCapital;
+
+pub struct FULLCAPITAL;
+
fn main() {}
-error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
--> $DIR/def_id_nocore.rs:26:19
|
LL | pub fn as_ref(self) -> &'static str {
| ^^^^
|
= note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+ = help: consider choosing a less ambiguous name
error: aborting due to previous error
+++ /dev/null
-// run-rustfix
-
-#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
-#![warn(clippy::explicit_deref_methods)]
-
-use std::ops::{Deref, DerefMut};
-
-fn concat(deref_str: &str) -> String {
- format!("{}bar", deref_str)
-}
-
-fn just_return(deref_str: &str) -> &str {
- deref_str
-}
-
-struct CustomVec(Vec<u8>);
-impl Deref for CustomVec {
- type Target = Vec<u8>;
-
- fn deref(&self) -> &Vec<u8> {
- &self.0
- }
-}
-
-fn main() {
- let a: &mut String = &mut String::from("foo");
-
- // these should require linting
-
- let b: &str = &*a;
-
- let b: &mut str = &mut *a;
-
- // both derefs should get linted here
- let b: String = format!("{}, {}", &*a, &*a);
-
- println!("{}", &*a);
-
- #[allow(clippy::match_single_binding)]
- match &*a {
- _ => (),
- }
-
- let b: String = concat(&*a);
-
- let b = &*just_return(a);
-
- let b: String = concat(&*just_return(a));
-
- let b: &str = &*a.deref();
-
- let opt_a = Some(a.clone());
- let b = &*opt_a.unwrap();
-
- // following should not require linting
-
- let cv = CustomVec(vec![0, 42]);
- let c = cv.deref()[0];
-
- let b: &str = &*a.deref();
-
- let b: String = a.deref().clone();
-
- let b: usize = a.deref_mut().len();
-
- let b: &usize = &a.deref().len();
-
- let b: &str = &*a;
-
- let b: &mut str = &mut *a;
-
- macro_rules! expr_deref {
- ($body:expr) => {
- $body.deref()
- };
- }
- let b: &str = expr_deref!(a);
-
- // The struct does not implement Deref trait
- #[derive(Copy, Clone)]
- struct NoLint(u32);
- impl NoLint {
- pub fn deref(self) -> u32 {
- self.0
- }
- pub fn deref_mut(self) -> u32 {
- self.0
- }
- }
- let no_lint = NoLint(42);
- let b = no_lint.deref();
- let b = no_lint.deref_mut();
-}
+++ /dev/null
-// run-rustfix
-
-#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
-#![warn(clippy::explicit_deref_methods)]
-
-use std::ops::{Deref, DerefMut};
-
-fn concat(deref_str: &str) -> String {
- format!("{}bar", deref_str)
-}
-
-fn just_return(deref_str: &str) -> &str {
- deref_str
-}
-
-struct CustomVec(Vec<u8>);
-impl Deref for CustomVec {
- type Target = Vec<u8>;
-
- fn deref(&self) -> &Vec<u8> {
- &self.0
- }
-}
-
-fn main() {
- let a: &mut String = &mut String::from("foo");
-
- // these should require linting
-
- let b: &str = a.deref();
-
- let b: &mut str = a.deref_mut();
-
- // both derefs should get linted here
- let b: String = format!("{}, {}", a.deref(), a.deref());
-
- println!("{}", a.deref());
-
- #[allow(clippy::match_single_binding)]
- match a.deref() {
- _ => (),
- }
-
- let b: String = concat(a.deref());
-
- let b = just_return(a).deref();
-
- let b: String = concat(just_return(a).deref());
-
- let b: &str = a.deref().deref();
-
- let opt_a = Some(a.clone());
- let b = opt_a.unwrap().deref();
-
- // following should not require linting
-
- let cv = CustomVec(vec![0, 42]);
- let c = cv.deref()[0];
-
- let b: &str = &*a.deref();
-
- let b: String = a.deref().clone();
-
- let b: usize = a.deref_mut().len();
-
- let b: &usize = &a.deref().len();
-
- let b: &str = &*a;
-
- let b: &mut str = &mut *a;
-
- macro_rules! expr_deref {
- ($body:expr) => {
- $body.deref()
- };
- }
- let b: &str = expr_deref!(a);
-
- // The struct does not implement Deref trait
- #[derive(Copy, Clone)]
- struct NoLint(u32);
- impl NoLint {
- pub fn deref(self) -> u32 {
- self.0
- }
- pub fn deref_mut(self) -> u32 {
- self.0
- }
- }
- let no_lint = NoLint(42);
- let b = no_lint.deref();
- let b = no_lint.deref_mut();
-}
+++ /dev/null
-error: explicit deref method call
- --> $DIR/dereference.rs:30:19
- |
-LL | let b: &str = a.deref();
- | ^^^^^^^^^ help: try this: `&*a`
- |
- = note: `-D clippy::explicit-deref-methods` implied by `-D warnings`
-
-error: explicit deref_mut method call
- --> $DIR/dereference.rs:32:23
- |
-LL | let b: &mut str = a.deref_mut();
- | ^^^^^^^^^^^^^ help: try this: `&mut *a`
-
-error: explicit deref method call
- --> $DIR/dereference.rs:35:39
- |
-LL | let b: String = format!("{}, {}", a.deref(), a.deref());
- | ^^^^^^^^^ help: try this: `&*a`
-
-error: explicit deref method call
- --> $DIR/dereference.rs:35:50
- |
-LL | let b: String = format!("{}, {}", a.deref(), a.deref());
- | ^^^^^^^^^ help: try this: `&*a`
-
-error: explicit deref method call
- --> $DIR/dereference.rs:37:20
- |
-LL | println!("{}", a.deref());
- | ^^^^^^^^^ help: try this: `&*a`
-
-error: explicit deref method call
- --> $DIR/dereference.rs:40:11
- |
-LL | match a.deref() {
- | ^^^^^^^^^ help: try this: `&*a`
-
-error: explicit deref method call
- --> $DIR/dereference.rs:44:28
- |
-LL | let b: String = concat(a.deref());
- | ^^^^^^^^^ help: try this: `&*a`
-
-error: explicit deref method call
- --> $DIR/dereference.rs:46:13
- |
-LL | let b = just_return(a).deref();
- | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)`
-
-error: explicit deref method call
- --> $DIR/dereference.rs:48:28
- |
-LL | let b: String = concat(just_return(a).deref());
- | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)`
-
-error: explicit deref method call
- --> $DIR/dereference.rs:50:19
- |
-LL | let b: &str = a.deref().deref();
- | ^^^^^^^^^^^^^^^^^ help: try this: `&*a.deref()`
-
-error: explicit deref method call
- --> $DIR/dereference.rs:53:13
- |
-LL | let b = opt_a.unwrap().deref();
- | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()`
-
-error: aborting due to 11 previous errors
-
--- /dev/null
+// run-rustfix
+
+#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
+#![warn(clippy::explicit_deref_methods)]
+
+use std::ops::{Deref, DerefMut};
+
+fn concat(deref_str: &str) -> String {
+ format!("{}bar", deref_str)
+}
+
+fn just_return(deref_str: &str) -> &str {
+ deref_str
+}
+
+struct CustomVec(Vec<u8>);
+impl Deref for CustomVec {
+ type Target = Vec<u8>;
+
+ fn deref(&self) -> &Vec<u8> {
+ &self.0
+ }
+}
+
+fn main() {
+ let a: &mut String = &mut String::from("foo");
+
+ // these should require linting
+
+ let b: &str = &*a;
+
+ let b: &mut str = &mut **a;
+
+ // both derefs should get linted here
+ let b: String = format!("{}, {}", &*a, &*a);
+
+ println!("{}", &*a);
+
+ #[allow(clippy::match_single_binding)]
+ match &*a {
+ _ => (),
+ }
+
+ let b: String = concat(&*a);
+
+ let b = just_return(a);
+
+ let b: String = concat(just_return(a));
+
+ let b: &str = &**a;
+
+ let opt_a = Some(a.clone());
+ let b = &*opt_a.unwrap();
+
+ // following should not require linting
+
+ let cv = CustomVec(vec![0, 42]);
+ let c = cv.deref()[0];
+
+ let b: &str = &*a.deref();
+
+ let b: String = a.deref().clone();
+
+ let b: usize = a.deref_mut().len();
+
+ let b: &usize = &a.deref().len();
+
+ let b: &str = &*a;
+
+ let b: &mut str = &mut *a;
+
+ macro_rules! expr_deref {
+ ($body:expr) => {
+ $body.deref()
+ };
+ }
+ let b: &str = expr_deref!(a);
+
+ let b: &str = expr_deref!(&*a);
+
+ // The struct does not implement Deref trait
+ #[derive(Copy, Clone)]
+ struct NoLint(u32);
+ impl NoLint {
+ pub fn deref(self) -> u32 {
+ self.0
+ }
+ pub fn deref_mut(self) -> u32 {
+ self.0
+ }
+ }
+ let no_lint = NoLint(42);
+ let b = no_lint.deref();
+ let b = no_lint.deref_mut();
+}
--- /dev/null
+// run-rustfix
+
+#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
+#![warn(clippy::explicit_deref_methods)]
+
+use std::ops::{Deref, DerefMut};
+
+fn concat(deref_str: &str) -> String {
+ format!("{}bar", deref_str)
+}
+
+fn just_return(deref_str: &str) -> &str {
+ deref_str
+}
+
+struct CustomVec(Vec<u8>);
+impl Deref for CustomVec {
+ type Target = Vec<u8>;
+
+ fn deref(&self) -> &Vec<u8> {
+ &self.0
+ }
+}
+
+fn main() {
+ let a: &mut String = &mut String::from("foo");
+
+ // these should require linting
+
+ let b: &str = a.deref();
+
+ let b: &mut str = a.deref_mut();
+
+ // both derefs should get linted here
+ let b: String = format!("{}, {}", a.deref(), a.deref());
+
+ println!("{}", a.deref());
+
+ #[allow(clippy::match_single_binding)]
+ match a.deref() {
+ _ => (),
+ }
+
+ let b: String = concat(a.deref());
+
+ let b = just_return(a).deref();
+
+ let b: String = concat(just_return(a).deref());
+
+ let b: &str = a.deref().deref();
+
+ let opt_a = Some(a.clone());
+ let b = opt_a.unwrap().deref();
+
+ // following should not require linting
+
+ let cv = CustomVec(vec![0, 42]);
+ let c = cv.deref()[0];
+
+ let b: &str = &*a.deref();
+
+ let b: String = a.deref().clone();
+
+ let b: usize = a.deref_mut().len();
+
+ let b: &usize = &a.deref().len();
+
+ let b: &str = &*a;
+
+ let b: &mut str = &mut *a;
+
+ macro_rules! expr_deref {
+ ($body:expr) => {
+ $body.deref()
+ };
+ }
+ let b: &str = expr_deref!(a);
+
+ let b: &str = expr_deref!(a.deref());
+
+ // The struct does not implement Deref trait
+ #[derive(Copy, Clone)]
+ struct NoLint(u32);
+ impl NoLint {
+ pub fn deref(self) -> u32 {
+ self.0
+ }
+ pub fn deref_mut(self) -> u32 {
+ self.0
+ }
+ }
+ let no_lint = NoLint(42);
+ let b = no_lint.deref();
+ let b = no_lint.deref_mut();
+}
--- /dev/null
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:30:19
+ |
+LL | let b: &str = a.deref();
+ | ^^^^^^^^^ help: try this: `&*a`
+ |
+ = note: `-D clippy::explicit-deref-methods` implied by `-D warnings`
+
+error: explicit `deref_mut` method call
+ --> $DIR/explicit_deref_methods.rs:32:23
+ |
+LL | let b: &mut str = a.deref_mut();
+ | ^^^^^^^^^^^^^ help: try this: `&mut **a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:35:39
+ |
+LL | let b: String = format!("{}, {}", a.deref(), a.deref());
+ | ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:35:50
+ |
+LL | let b: String = format!("{}, {}", a.deref(), a.deref());
+ | ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:37:20
+ |
+LL | println!("{}", a.deref());
+ | ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:40:11
+ |
+LL | match a.deref() {
+ | ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:44:28
+ |
+LL | let b: String = concat(a.deref());
+ | ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:46:13
+ |
+LL | let b = just_return(a).deref();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:48:28
+ |
+LL | let b: String = concat(just_return(a).deref());
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:50:19
+ |
+LL | let b: &str = a.deref().deref();
+ | ^^^^^^^^^^^^^^^^^ help: try this: `&**a`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:53:13
+ |
+LL | let b = opt_a.unwrap().deref();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()`
+
+error: explicit `deref` method call
+ --> $DIR/explicit_deref_methods.rs:79:31
+ |
+LL | let b: &str = expr_deref!(a.deref());
+ | ^^^^^^^^^ help: try this: `&*a`
+
+error: aborting due to 12 previous errors
+
// Don't lint in external macros
field_reassign_with_default!();
+
+ // be sure suggestion is correct with generics
+ let mut a: Wrapper<bool> = Default::default();
+ a.i = true;
+
+ let mut a: WrapperMulti<i32, i64> = Default::default();
+ a.i = 42;
}
mod m {
b: u64,
}
}
+
+#[derive(Default)]
+struct Wrapper<T> {
+ i: T,
+}
+
+#[derive(Default)]
+struct WrapperMulti<T, U> {
+ i: T,
+ j: U,
+}
LL | let mut a: C = C::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 7 previous errors
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:142:5
+ |
+LL | a.i = true;
+ | ^^^^^^^^^^^
+ |
+note: consider initializing the variable with `Wrapper::<bool> { i: true }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:141:5
+ |
+LL | let mut a: Wrapper<bool> = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+ --> $DIR/field_reassign_with_default.rs:145:5
+ |
+LL | a.i = 42;
+ | ^^^^^^^^^
+ |
+note: consider initializing the variable with `WrapperMulti::<i32, i64> { i: 42, ..Default::default() }` and removing relevant reassignments
+ --> $DIR/field_reassign_with_default.rs:144:5
+ |
+LL | let mut a: WrapperMulti<i32, i64> = Default::default();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
--- /dev/null
+#![warn(clippy::if_then_some_else_none)]
+#![feature(custom_inner_attributes)]
+
+fn main() {
+ // Should issue an error.
+ let _ = if foo() {
+ println!("true!");
+ Some("foo")
+ } else {
+ None
+ };
+
+ // Should issue an error when macros are used.
+ let _ = if matches!(true, true) {
+ println!("true!");
+ Some(matches!(true, false))
+ } else {
+ None
+ };
+
+ // Should issue an error. Binary expression `o < 32` should be parenthesized.
+ let x = Some(5);
+ let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
+
+ // Should issue an error. Unary expression `!x` should be parenthesized.
+ let x = true;
+ let _ = if !x { Some(0) } else { None };
+
+ // Should not issue an error since the `else` block has a statement besides `None`.
+ let _ = if foo() {
+ println!("true!");
+ Some("foo")
+ } else {
+ eprintln!("false...");
+ None
+ };
+
+ // Should not issue an error since there are more than 2 blocks in the if-else chain.
+ let _ = if foo() {
+ println!("foo true!");
+ Some("foo")
+ } else if bar() {
+ println!("bar true!");
+ Some("bar")
+ } else {
+ None
+ };
+
+ let _ = if foo() {
+ println!("foo true!");
+ Some("foo")
+ } else {
+ bar().then(|| {
+ println!("bar true!");
+ "bar"
+ })
+ };
+
+ // Should not issue an error since the `then` block has `None`, not `Some`.
+ let _ = if foo() { None } else { Some("foo is false") };
+
+ // Should not issue an error since the `else` block doesn't use `None` directly.
+ let _ = if foo() { Some("foo is true") } else { into_none() };
+
+ // Should not issue an error since the `then` block doesn't use `Some` directly.
+ let _ = if foo() { into_some("foo") } else { None };
+}
+
+fn _msrv_1_49() {
+ #![clippy::msrv = "1.49"]
+ // `bool::then` was stabilized in 1.50. Do not lint this
+ let _ = if foo() {
+ println!("true!");
+ Some(149)
+ } else {
+ None
+ };
+}
+
+fn _msrv_1_50() {
+ #![clippy::msrv = "1.50"]
+ let _ = if foo() {
+ println!("true!");
+ Some(150)
+ } else {
+ None
+ };
+}
+
+fn foo() -> bool {
+ unimplemented!()
+}
+
+fn bar() -> bool {
+ unimplemented!()
+}
+
+fn into_some<T>(v: T) -> Option<T> {
+ Some(v)
+}
+
+fn into_none<T>() -> Option<T> {
+ None
+}
--- /dev/null
+error: this could be simplified with `bool::then`
+ --> $DIR/if_then_some_else_none.rs:6:13
+ |
+LL | let _ = if foo() {
+ | _____________^
+LL | | println!("true!");
+LL | | Some("foo")
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^
+ |
+ = note: `-D clippy::if-then-some-else-none` implied by `-D warnings`
+ = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ "foo" })`
+
+error: this could be simplified with `bool::then`
+ --> $DIR/if_then_some_else_none.rs:14:13
+ |
+LL | let _ = if matches!(true, true) {
+ | _____________^
+LL | | println!("true!");
+LL | | Some(matches!(true, false))
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^
+ |
+ = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })`
+
+error: this could be simplified with `bool::then`
+ --> $DIR/if_then_some_else_none.rs:23:28
+ |
+LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using `bool::then` like: `(o < 32).then(|| o)`
+
+error: this could be simplified with `bool::then`
+ --> $DIR/if_then_some_else_none.rs:27:13
+ |
+LL | let _ = if !x { Some(0) } else { None };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using `bool::then` like: `(!x).then(|| 0)`
+
+error: this could be simplified with `bool::then`
+ --> $DIR/if_then_some_else_none.rs:82:13
+ |
+LL | let _ = if foo() {
+ | _____________^
+LL | | println!("true!");
+LL | | Some(150)
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^
+ |
+ = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ 150 })`
+
+error: aborting due to 5 previous errors
+
-error: inconsistent struct constructor
+error: struct constructor field order is inconsistent with struct definition field order
--> $DIR/inconsistent_struct_constructor.rs:25:9
|
LL | Foo { y, x, z };
|
= note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings`
-error: inconsistent struct constructor
+error: struct constructor field order is inconsistent with struct definition field order
--> $DIR/inconsistent_struct_constructor.rs:43:9
|
LL | / Foo {
#![warn(clippy::manual_flatten)]
+#![allow(clippy::useless_vec)]
fn main() {
// Test for loop over implicitly adjusted `Iterator` with `if let` expression
}
}
+ let vec_of_ref = vec![&Some(1)];
+ for n in &vec_of_ref {
+ if let Some(n) = n {
+ println!("{:?}", n);
+ }
+ }
+
+ let vec_of_ref = &vec_of_ref;
+ for n in vec_of_ref {
+ if let Some(n) = n {
+ println!("{:?}", n);
+ }
+ }
+
+ let slice_of_ref = &[&Some(1)];
+ for n in slice_of_ref {
+ if let Some(n) = n {
+ println!("{:?}", n);
+ }
+ }
+
// Using manual flatten should not trigger the lint
for n in vec![Some(1), Some(2), Some(3)].iter().flatten() {
println!("{}", n);
error: unnecessary `if let` since only the `Some` variant of the iterator element is used
- --> $DIR/manual_flatten.rs:6:5
+ --> $DIR/manual_flatten.rs:7:5
|
LL | for n in x {
| ^ - help: try: `x.into_iter().flatten()`
|
= note: `-D clippy::manual-flatten` implied by `-D warnings`
help: ...and remove the `if let` statement in the for loop
- --> $DIR/manual_flatten.rs:7:9
+ --> $DIR/manual_flatten.rs:8:9
|
LL | / if let Some(y) = n {
LL | | println!("{}", y);
| |_________^
error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
- --> $DIR/manual_flatten.rs:14:5
+ --> $DIR/manual_flatten.rs:15:5
|
LL | for n in y.clone() {
| ^ --------- help: try: `y.clone().into_iter().flatten()`
| |_____^
|
help: ...and remove the `if let` statement in the for loop
- --> $DIR/manual_flatten.rs:15:9
+ --> $DIR/manual_flatten.rs:16:9
|
LL | / if let Ok(n) = n {
LL | | println!("{}", n);
| |_________^
error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
- --> $DIR/manual_flatten.rs:21:5
+ --> $DIR/manual_flatten.rs:22:5
|
LL | for n in &y {
| ^ -- help: try: `y.iter().flatten()`
| |_____^
|
help: ...and remove the `if let` statement in the for loop
- --> $DIR/manual_flatten.rs:22:9
+ --> $DIR/manual_flatten.rs:23:9
|
LL | / if let Ok(n) = n {
LL | | println!("{}", n);
| |_________^
error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
- --> $DIR/manual_flatten.rs:31:5
+ --> $DIR/manual_flatten.rs:32:5
|
LL | for n in z {
| ^ - help: try: `z.into_iter().flatten()`
| |_____^
|
help: ...and remove the `if let` statement in the for loop
- --> $DIR/manual_flatten.rs:32:9
+ --> $DIR/manual_flatten.rs:33:9
|
LL | / if let Ok(n) = n {
LL | | println!("{}", n);
| |_________^
error: unnecessary `if let` since only the `Some` variant of the iterator element is used
- --> $DIR/manual_flatten.rs:40:5
+ --> $DIR/manual_flatten.rs:41:5
|
LL | for n in z {
| ^ - help: try: `z.flatten()`
| |_____^
|
help: ...and remove the `if let` statement in the for loop
- --> $DIR/manual_flatten.rs:41:9
+ --> $DIR/manual_flatten.rs:42:9
|
LL | / if let Some(m) = n {
LL | | println!("{}", m);
LL | | }
| |_________^
-error: aborting due to 5 previous errors
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+ --> $DIR/manual_flatten.rs:74:5
+ |
+LL | for n in &vec_of_ref {
+ | ^ ----------- help: try: `vec_of_ref.iter().copied().flatten()`
+ | _____|
+ | |
+LL | | if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: ...and remove the `if let` statement in the for loop
+ --> $DIR/manual_flatten.rs:75:9
+ |
+LL | / if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+ | |_________^
+
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+ --> $DIR/manual_flatten.rs:81:5
+ |
+LL | for n in vec_of_ref {
+ | ^ ---------- help: try: `vec_of_ref.into_iter().copied().flatten()`
+ | _____|
+ | |
+LL | | if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: ...and remove the `if let` statement in the for loop
+ --> $DIR/manual_flatten.rs:82:9
+ |
+LL | / if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+ | |_________^
+
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+ --> $DIR/manual_flatten.rs:88:5
+ |
+LL | for n in slice_of_ref {
+ | ^ ------------ help: try: `slice_of_ref.into_iter().copied().flatten()`
+ | _____|
+ | |
+LL | | if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+LL | | }
+ | |_____^
+ |
+help: ...and remove the `if let` statement in the for loop
+ --> $DIR/manual_flatten.rs:89:9
+ |
+LL | / if let Some(n) = n {
+LL | | println!("{:?}", n);
+LL | | }
+ | |_________^
+
+error: aborting due to 8 previous errors
None => None,
};
}
+
+ // #6847
+ if Some(0).is_some() {
+ Some(0)
+ } else { Some(0).map(|x| x + 1) };
}
None => None,
};
}
+
+ // #6847
+ if let Some(_) = Some(0) {
+ Some(0)
+ } else if let Some(x) = Some(0) {
+ Some(x + 1)
+ } else {
+ None
+ };
}
LL | | };
| |_____^ help: try this: `option_env!("").map(String::from)`
-error: aborting due to 19 previous errors
+error: redundant pattern matching, consider using `is_some()`
+ --> $DIR/manual_map_option.rs:191:12
+ |
+LL | if let Some(_) = Some(0) {
+ | -------^^^^^^^---------- help: try this: `if Some(0).is_some()`
+ |
+ = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option.rs:193:12
+ |
+LL | } else if let Some(x) = Some(0) {
+ | ____________^
+LL | | Some(x + 1)
+LL | | } else {
+LL | | None
+LL | | };
+ | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }`
+
+error: aborting due to 21 previous errors
};
}
+// don't lint in const fn
+const fn const_fn_option_unwrap_or() {
+ match Some(1) {
+ Some(s) => s,
+ None => 0,
+ };
+}
+
+const fn const_fn_result_unwrap_or() {
+ match Ok::<&str, &str>("Alice") {
+ Ok(s) => s,
+ Err(_) => "Bob",
+ };
+}
+
fn main() {}
};
}
+// don't lint in const fn
+const fn const_fn_option_unwrap_or() {
+ match Some(1) {
+ Some(s) => s,
+ None => 0,
+ };
+}
+
+const fn const_fn_result_unwrap_or() {
+ match Ok::<&str, &str>("Alice") {
+ Ok(s) => s,
+ Err(_) => "Bob",
+ };
+}
+
fn main() {}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::match_single_binding)]
+#![allow(unused_variables)]
+
+fn main() {
+ // Lint (additional curly braces needed, see #6572)
+ struct AppendIter<I>
+ where
+ I: Iterator,
+ {
+ inner: Option<(I, <I as Iterator>::Item)>,
+ }
+
+ #[allow(dead_code)]
+ fn size_hint<I: Iterator>(iter: &AppendIter<I>) -> (usize, Option<usize>) {
+ match &iter.inner {
+ Some((iter, _item)) => {
+ let (min, max) = iter.size_hint();
+ (min.saturating_add(1), max.and_then(|max| max.checked_add(1)))
+ },
+ None => (0, Some(0)),
+ }
+ }
+
+ // Lint (no additional curly braces needed)
+ let opt = Some((5, 2));
+ let get_tup = || -> (i32, i32) { (1, 2) };
+ match opt {
+ #[rustfmt::skip]
+ Some((first, _second)) => {
+ let (a, b) = get_tup();
+ println!("a {:?} and b {:?}", a, b);
+ },
+ None => println!("nothing"),
+ }
+}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::match_single_binding)]
+#![allow(unused_variables)]
+
+fn main() {
+ // Lint (additional curly braces needed, see #6572)
+ struct AppendIter<I>
+ where
+ I: Iterator,
+ {
+ inner: Option<(I, <I as Iterator>::Item)>,
+ }
+
+ #[allow(dead_code)]
+ fn size_hint<I: Iterator>(iter: &AppendIter<I>) -> (usize, Option<usize>) {
+ match &iter.inner {
+ Some((iter, _item)) => match iter.size_hint() {
+ (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))),
+ },
+ None => (0, Some(0)),
+ }
+ }
+
+ // Lint (no additional curly braces needed)
+ let opt = Some((5, 2));
+ let get_tup = || -> (i32, i32) { (1, 2) };
+ match opt {
+ #[rustfmt::skip]
+ Some((first, _second)) => {
+ match get_tup() {
+ (a, b) => println!("a {:?} and b {:?}", a, b),
+ }
+ },
+ None => println!("nothing"),
+ }
+}
--- /dev/null
+error: this match could be written as a `let` statement
+ --> $DIR/match_single_binding2.rs:18:36
+ |
+LL | Some((iter, _item)) => match iter.size_hint() {
+ | ____________________________________^
+LL | | (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))),
+LL | | },
+ | |_____________^
+ |
+ = note: `-D clippy::match-single-binding` implied by `-D warnings`
+help: consider using `let` statement
+ |
+LL | Some((iter, _item)) => {
+LL | let (min, max) = iter.size_hint();
+LL | (min.saturating_add(1), max.and_then(|max| max.checked_add(1)))
+LL | },
+ |
+
+error: this match could be written as a `let` statement
+ --> $DIR/match_single_binding2.rs:31:13
+ |
+LL | / match get_tup() {
+LL | | (a, b) => println!("a {:?} and b {:?}", a, b),
+LL | | }
+ | |_____________^
+ |
+help: consider using `let` statement
+ |
+LL | let (a, b) = get_tup();
+LL | println!("a {:?} and b {:?}", a, b);
+ |
+
+error: aborting due to 2 previous errors
+
Blue,
Rgb(u8, u8, u8),
}
+impl Color {
+ fn f(self) {
+ match self {
+ Self::Red => (),
+ Self::Green => (),
+ Self::Blue => (),
+ Self::Rgb(..) => (),
+ };
+ }
+}
fn main() {
let f = Foo::A;
Color::Rgb(255, _, _) => {},
_ => {},
}
+
+ // References shouldn't change anything
+ match &color {
+ &Color::Red => (),
+ Color::Green => (),
+ &Color::Rgb(..) => (),
+ Color::Blue => (),
+ }
+
+ use self::Color as C;
+
+ match color {
+ C::Red => (),
+ C::Green => (),
+ C::Rgb(..) => (),
+ C::Blue => (),
+ }
+
+ match color {
+ C::Red => (),
+ Color::Green => (),
+ Color::Rgb(..) => (),
+ Color::Blue => (),
+ }
+
+ match Some(0) {
+ Some(0) => 0,
+ Some(_) => 1,
+ _ => 2,
+ };
+
+ #[non_exhaustive]
+ enum Bar {
+ A,
+ B,
+ C,
+ }
+ match Bar::A {
+ Bar::A => (),
+ Bar::B => (),
+ _ => (),
+ };
}
Blue,
Rgb(u8, u8, u8),
}
+impl Color {
+ fn f(self) {
+ match self {
+ Self::Red => (),
+ Self::Green => (),
+ Self::Blue => (),
+ _ => (),
+ };
+ }
+}
fn main() {
let f = Foo::A;
Color::Rgb(255, _, _) => {},
_ => {},
}
+
+ // References shouldn't change anything
+ match &color {
+ &Color::Red => (),
+ Color::Green => (),
+ &Color::Rgb(..) => (),
+ &_ => (),
+ }
+
+ use self::Color as C;
+
+ match color {
+ C::Red => (),
+ C::Green => (),
+ C::Rgb(..) => (),
+ _ => (),
+ }
+
+ match color {
+ C::Red => (),
+ Color::Green => (),
+ Color::Rgb(..) => (),
+ _ => (),
+ }
+
+ match Some(0) {
+ Some(0) => 0,
+ Some(_) => 1,
+ _ => 2,
+ };
+
+ #[non_exhaustive]
+ enum Bar {
+ A,
+ B,
+ C,
+ }
+ match Bar::A {
+ Bar::A => (),
+ Bar::B => (),
+ _ => (),
+ };
}
-error: wildcard match will miss any future added variants
- --> $DIR/match_wildcard_for_single_variants.rs:24:9
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:24:13
|
-LL | _ => {},
- | ^ help: try this: `Foo::C`
+LL | _ => (),
+ | ^ help: try this: `Self::Rgb(..)`
|
= note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings`
-error: wildcard match will miss any future added variants
+error: wildcard matches only a single variant and will also match any future added variants
--> $DIR/match_wildcard_for_single_variants.rs:34:9
|
+LL | _ => {},
+ | ^ help: try this: `Foo::C`
+
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:44:9
+ |
LL | _ => {},
| ^ help: try this: `Color::Blue`
-error: wildcard match will miss any future added variants
- --> $DIR/match_wildcard_for_single_variants.rs:42:9
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:52:9
|
LL | _ => {},
| ^ help: try this: `Color::Blue`
-error: wildcard match will miss any future added variants
- --> $DIR/match_wildcard_for_single_variants.rs:48:9
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:58:9
|
LL | _ => {},
| ^ help: try this: `Color::Blue`
-error: aborting due to 4 previous errors
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:75:9
+ |
+LL | &_ => (),
+ | ^^ help: try this: `Color::Blue`
+
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:84:9
+ |
+LL | _ => (),
+ | ^ help: try this: `C::Blue`
+
+error: wildcard matches only a single variant and will also match any future added variants
+ --> $DIR/match_wildcard_for_single_variants.rs:91:9
+ |
+LL | _ => (),
+ | ^ help: try this: `Color::Blue`
+
+error: aborting due to 8 previous errors
clippy::mem_replace_with_default
)]
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
use std::mem;
fn replace_option_with_none() {
fn replace_with_default() {
let mut s = String::from("foo");
let _ = std::mem::take(&mut s);
+
let s = &mut String::from("foo");
let _ = std::mem::take(s);
let _ = std::mem::take(s);
+
+ let mut v = vec![123];
+ let _ = std::mem::take(&mut v);
+ let _ = std::mem::take(&mut v);
+ let _ = std::mem::take(&mut v);
+ let _ = std::mem::take(&mut v);
+
+ let mut hash_map: HashMap<i32, i32> = HashMap::new();
+ let _ = std::mem::take(&mut hash_map);
+
+ let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new();
+ let _ = std::mem::take(&mut btree_map);
+
+ let mut vd: VecDeque<i32> = VecDeque::new();
+ let _ = std::mem::take(&mut vd);
+
+ let mut hash_set: HashSet<&str> = HashSet::new();
+ let _ = std::mem::take(&mut hash_set);
+
+ let mut btree_set: BTreeSet<&str> = BTreeSet::new();
+ let _ = std::mem::take(&mut btree_set);
+
+ let mut list: LinkedList<i32> = LinkedList::new();
+ let _ = std::mem::take(&mut list);
+
+ let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
+ let _ = std::mem::take(&mut binary_heap);
}
fn main() {
clippy::mem_replace_with_default
)]
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
use std::mem;
fn replace_option_with_none() {
fn replace_with_default() {
let mut s = String::from("foo");
let _ = std::mem::replace(&mut s, String::default());
+
let s = &mut String::from("foo");
let _ = std::mem::replace(s, String::default());
let _ = std::mem::replace(s, Default::default());
+
+ let mut v = vec![123];
+ let _ = std::mem::replace(&mut v, Vec::default());
+ let _ = std::mem::replace(&mut v, Default::default());
+ let _ = std::mem::replace(&mut v, Vec::new());
+ let _ = std::mem::replace(&mut v, vec![]);
+
+ let mut hash_map: HashMap<i32, i32> = HashMap::new();
+ let _ = std::mem::replace(&mut hash_map, HashMap::new());
+
+ let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new();
+ let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
+
+ let mut vd: VecDeque<i32> = VecDeque::new();
+ let _ = std::mem::replace(&mut vd, VecDeque::new());
+
+ let mut hash_set: HashSet<&str> = HashSet::new();
+ let _ = std::mem::replace(&mut hash_set, HashSet::new());
+
+ let mut btree_set: BTreeSet<&str> = BTreeSet::new();
+ let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
+
+ let mut list: LinkedList<i32> = LinkedList::new();
+ let _ = std::mem::replace(&mut list, LinkedList::new());
+
+ let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
+ let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
}
fn main() {
error: replacing an `Option` with `None`
- --> $DIR/mem_replace.rs:14:13
+ --> $DIR/mem_replace.rs:15:13
|
LL | let _ = mem::replace(&mut an_option, None);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
= note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings`
error: replacing an `Option` with `None`
- --> $DIR/mem_replace.rs:16:13
+ --> $DIR/mem_replace.rs:17:13
|
LL | let _ = mem::replace(an_option, None);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:21:13
+ --> $DIR/mem_replace.rs:22:13
|
LL | let _ = std::mem::replace(&mut s, String::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
= note: `-D clippy::mem-replace-with-default` implied by `-D warnings`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:23:13
+ --> $DIR/mem_replace.rs:25:13
|
LL | let _ = std::mem::replace(s, String::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace.rs:24:13
+ --> $DIR/mem_replace.rs:26:13
|
LL | let _ = std::mem::replace(s, Default::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
-error: aborting due to 5 previous errors
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:29:13
+ |
+LL | let _ = std::mem::replace(&mut v, Vec::default());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:30:13
+ |
+LL | let _ = std::mem::replace(&mut v, Default::default());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:31:13
+ |
+LL | let _ = std::mem::replace(&mut v, Vec::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:32:13
+ |
+LL | let _ = std::mem::replace(&mut v, vec![]);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:35:13
+ |
+LL | let _ = std::mem::replace(&mut hash_map, HashMap::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:38:13
+ |
+LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:41:13
+ |
+LL | let _ = std::mem::replace(&mut vd, VecDeque::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:44:13
+ |
+LL | let _ = std::mem::replace(&mut hash_set, HashSet::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:47:13
+ |
+LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:50:13
+ |
+LL | let _ = std::mem::replace(&mut list, LinkedList::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:53:13
+ |
+LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)`
+
+error: aborting due to 16 previous errors
needless_question_mark_result();
}
}
+
+// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use,
+// the suggestion fails to apply; do not lint
+macro_rules! some_in_macro {
+ ($expr:expr) => {
+ || -> _ { Some($expr) }()
+ };
+}
+
+pub fn test1() {
+ let x = Some(3);
+ let _x = some_in_macro!(x?);
+}
+
+// this one is ok because both the ? and the Some are both inside the macro def
+macro_rules! some_and_qmark_in_macro {
+ ($expr:expr) => {
+ || -> Option<_> { Some($expr) }()
+ };
+}
+
+pub fn test2() {
+ let x = Some(3);
+ let _x = some_and_qmark_in_macro!(x?);
+}
needless_question_mark_result();
}
}
+
+// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use,
+// the suggestion fails to apply; do not lint
+macro_rules! some_in_macro {
+ ($expr:expr) => {
+ || -> _ { Some($expr) }()
+ };
+}
+
+pub fn test1() {
+ let x = Some(3);
+ let _x = some_in_macro!(x?);
+}
+
+// this one is ok because both the ? and the Some are both inside the macro def
+macro_rules! some_and_qmark_in_macro {
+ ($expr:expr) => {
+ || -> Option<_> { Some(Some($expr)?) }()
+ };
+}
+
+pub fn test2() {
+ let x = Some(3);
+ let _x = some_and_qmark_in_macro!(x?);
+}
LL | Ok(to.magic?) // should be triggered
| ^^^^^^^^^^^^^ help: try: `to.magic`
-error: aborting due to 14 previous errors
+error: question mark operator is useless here
+ --> $DIR/needless_question_mark.rs:187:27
+ |
+LL | || -> Option<_> { Some(Some($expr)?) }()
+ | ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)`
+...
+LL | let _x = some_and_qmark_in_macro!(x?);
+ | ---------------------------- 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 15 previous errors
}
}
}
+
+// issue #1724
+struct RetOtherSelf<T>(T);
+struct RetOtherSelfWrapper<T>(T);
+
+impl RetOtherSelf<T> {
+ fn new(t: T) -> RetOtherSelf<RetOtherSelfWrapper<T>> {
+ RetOtherSelf(RetOtherSelfWrapper(t))
+ }
+}
}
}
+// see #6933
+pub struct FooGenerics<T>(std::marker::PhantomData<T>);
+impl<T> FooGenerics<T> {
+ pub fn new() -> Self {
+ Self(Default::default())
+ }
+}
+
+pub struct BarGenerics<T>(std::marker::PhantomData<T>);
+impl<T: Copy> BarGenerics<T> {
+ pub fn new() -> Self {
+ Self(Default::default())
+ }
+}
+
fn main() {}
|
help: try this
|
-LL | impl Default for LtKo<'c> {
+LL | impl<'c> Default for LtKo<'c> {
LL | fn default() -> Self {
LL | Self::new()
LL | }
LL | }
|
-error: aborting due to 4 previous errors
+error: you should consider adding a `Default` implementation for `FooGenerics<T>`
+ --> $DIR/new_without_default.rs:165:5
+ |
+LL | / pub fn new() -> Self {
+LL | | Self(Default::default())
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | impl<T> Default for FooGenerics<T> {
+LL | fn default() -> Self {
+LL | Self::new()
+LL | }
+LL | }
+ |
+
+error: you should consider adding a `Default` implementation for `BarGenerics<T>`
+ --> $DIR/new_without_default.rs:172:5
+ |
+LL | / pub fn new() -> Self {
+LL | | Self(Default::default())
+LL | | }
+ | |_____^
+ |
+help: try this
+ |
+LL | impl<T: Copy> Default for BarGenerics<T> {
+LL | fn default() -> Self {
+LL | Self::new()
+LL | }
+LL | }
+ |
+
+error: aborting due to 6 previous errors
let slice = &["foo"][..];
let _ = opt.ok_or(slice.len());
+
+ let string = "foo";
+ let _ = opt.ok_or(string.len());
}
// Issue 4514 - early return
Some(())
}
+mod issue6675 {
+ unsafe fn foo() {
+ let mut s = "test".to_owned();
+ None.unwrap_or_else(|| s.as_mut_vec());
+ }
+
+ fn bar() {
+ let mut s = "test".to_owned();
+ None.unwrap_or_else(|| unsafe { s.as_mut_vec() });
+ #[rustfmt::skip]
+ None.unwrap_or_else(|| unsafe { s.as_mut_vec() });
+ }
+}
+
fn main() {}
let slice = &["foo"][..];
let _ = opt.ok_or(slice.len());
+
+ let string = "foo";
+ let _ = opt.ok_or(string.len());
}
// Issue 4514 - early return
Some(())
}
+mod issue6675 {
+ unsafe fn foo() {
+ let mut s = "test".to_owned();
+ None.unwrap_or(s.as_mut_vec());
+ }
+
+ fn bar() {
+ let mut s = "test".to_owned();
+ None.unwrap_or(unsafe { s.as_mut_vec() });
+ #[rustfmt::skip]
+ None.unwrap_or( unsafe { s.as_mut_vec() } );
+ }
+}
+
fn main() {}
LL | .or(Some(Bar(b, Duration::from_secs(2))));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))`
-error: aborting due to 19 previous errors
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:141:14
+ |
+LL | None.unwrap_or(s.as_mut_vec());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| s.as_mut_vec())`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:146:14
+ |
+LL | None.unwrap_or(unsafe { s.as_mut_vec() });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })`
+
+error: use of `unwrap_or` followed by a function call
+ --> $DIR/or_fun_call.rs:148:14
+ |
+LL | None.unwrap_or( unsafe { s.as_mut_vec() } );
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })`
+
+error: aborting due to 22 previous errors
error: use of `Debug`-based formatting
- --> $DIR/print.rs:11:19
+ --> $DIR/print.rs:11:20
|
LL | write!(f, "{:?}", 43.1415)
- | ^^^^^^
+ | ^^^^
|
= note: `-D clippy::use-debug` implied by `-D warnings`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: use of `Debug`-based formatting
- --> $DIR/print.rs:28:12
+ --> $DIR/print.rs:28:19
|
LL | print!("Hello {:?}", "World");
- | ^^^^^^^^^^^^
+ | ^^^^
error: use of `print!`
--> $DIR/print.rs:30:5
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: use of `Debug`-based formatting
- --> $DIR/print.rs:30:12
+ --> $DIR/print.rs:30:19
|
LL | print!("Hello {:#?}", "#orld");
- | ^^^^^^^^^^^^^
+ | ^^^^^
error: aborting due to 8 previous errors
| ^^^^^^^
|
= note: `-D clippy::print-literal` implied by `-D warnings`
+help: try this
+ |
+LL | print!("Hello world");
+ | ^^^^^--
error: literal with an empty format string
--> $DIR/print_literal.rs:26:36
|
LL | println!("Hello {} {}", world, "world");
| ^^^^^^^
+ |
+help: try this
+ |
+LL | println!("Hello {} world", world);
+ | ^^^^^ --
error: literal with an empty format string
--> $DIR/print_literal.rs:27:26
|
LL | println!("Hello {}", "world");
| ^^^^^^^
+ |
+help: try this
+ |
+LL | println!("Hello world");
+ | ^^^^^--
error: literal with an empty format string
--> $DIR/print_literal.rs:32:25
|
LL | println!("{0} {1}", "hello", "world");
| ^^^^^^^
+ |
+help: try this
+ |
+LL | println!("hello {1}", "world");
+ | ^^^^^ --
error: literal with an empty format string
--> $DIR/print_literal.rs:32:34
|
LL | println!("{0} {1}", "hello", "world");
| ^^^^^^^
+ |
+help: try this
+ |
+LL | println!("{0} world", "hello");
+ | ^^^^^ --
error: literal with an empty format string
--> $DIR/print_literal.rs:33:25
|
LL | println!("{1} {0}", "hello", "world");
| ^^^^^^^
+ |
+help: try this
+ |
+LL | println!("{1} hello", "world");
+ | ^^^^^--
error: literal with an empty format string
--> $DIR/print_literal.rs:33:34
|
LL | println!("{1} {0}", "hello", "world");
| ^^^^^^^
+ |
+help: try this
+ |
+LL | println!("world {0}", "hello");
+ | ^^^^^ --
error: literal with an empty format string
- --> $DIR/print_literal.rs:36:35
+ --> $DIR/print_literal.rs:36:29
|
LL | println!("{foo} {bar}", foo = "hello", bar = "world");
- | ^^^^^^^
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL | println!("hello {bar}", bar = "world");
+ | ^^^^^ --
error: literal with an empty format string
- --> $DIR/print_literal.rs:36:50
+ --> $DIR/print_literal.rs:36:44
|
LL | println!("{foo} {bar}", foo = "hello", bar = "world");
- | ^^^^^^^
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL | println!("{foo} world", foo = "hello");
+ | ^^^^^ --
error: literal with an empty format string
- --> $DIR/print_literal.rs:37:35
+ --> $DIR/print_literal.rs:37:29
|
LL | println!("{bar} {foo}", foo = "hello", bar = "world");
- | ^^^^^^^
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL | println!("{bar} hello", bar = "world");
+ | ^^^^^--
error: literal with an empty format string
- --> $DIR/print_literal.rs:37:50
+ --> $DIR/print_literal.rs:37:44
|
LL | println!("{bar} {foo}", foo = "hello", bar = "world");
- | ^^^^^^^
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL | println!("world {foo}", foo = "hello");
+ | ^^^^^ --
error: aborting due to 11 previous errors
// aux-build:option_helpers.rs
+#![warn(clippy::search_is_some)]
+#![allow(dead_code)]
extern crate option_helpers;
use option_helpers::IteratorFalsePositives;
-#[warn(clippy::search_is_some)]
#[rustfmt::skip]
fn main() {
let v = vec![3, 2, 1, 0, -1, -2, -3];
// `Pattern` that is not a string
let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some();
}
+
+#[rustfmt::skip]
+fn is_none() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+ let y = &&42;
+
+
+ // Check `find().is_none()`, multi-line case.
+ let _ = v.iter().find(|&x| {
+ *x < 0
+ }
+ ).is_none();
+
+ // Check `position().is_none()`, multi-line case.
+ let _ = v.iter().position(|&x| {
+ x < 0
+ }
+ ).is_none();
+
+ // Check `rposition().is_none()`, multi-line case.
+ let _ = v.iter().rposition(|&x| {
+ x < 0
+ }
+ ).is_none();
+
+ // Check that we don't lint if the caller is not an `Iterator` or string
+ let falsepos = IteratorFalsePositives { foo: 0 };
+ let _ = falsepos.find().is_none();
+ let _ = falsepos.position().is_none();
+ let _ = falsepos.rposition().is_none();
+ // check that we don't lint if `find()` is called with
+ // `Pattern` that is not a string
+ let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_none();
+}
error: called `is_some()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some.rs:13:13
+ --> $DIR/search_is_some.rs:14:13
|
LL | let _ = v.iter().find(|&x| {
| _____________^
= help: this is more succinctly expressed by calling `any()`
error: called `is_some()` after searching an `Iterator` with `position`
- --> $DIR/search_is_some.rs:19:13
+ --> $DIR/search_is_some.rs:20:13
|
LL | let _ = v.iter().position(|&x| {
| _____________^
= help: this is more succinctly expressed by calling `any()`
error: called `is_some()` after searching an `Iterator` with `rposition`
- --> $DIR/search_is_some.rs:25:13
+ --> $DIR/search_is_some.rs:26:13
|
LL | let _ = v.iter().rposition(|&x| {
| _____________^
|
= help: this is more succinctly expressed by calling `any()`
-error: aborting due to 3 previous errors
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some.rs:48:13
+ |
+LL | let _ = v.iter().find(|&x| {
+ | _____________^
+LL | | *x < 0
+LL | | }
+LL | | ).is_none();
+ | |______________________________^
+ |
+ = help: this is more succinctly expressed by calling `any()` with negation
+
+error: called `is_none()` after searching an `Iterator` with `position`
+ --> $DIR/search_is_some.rs:54:13
+ |
+LL | let _ = v.iter().position(|&x| {
+ | _____________^
+LL | | x < 0
+LL | | }
+LL | | ).is_none();
+ | |______________________________^
+ |
+ = help: this is more succinctly expressed by calling `any()` with negation
+
+error: called `is_none()` after searching an `Iterator` with `rposition`
+ --> $DIR/search_is_some.rs:60:13
+ |
+LL | let _ = v.iter().rposition(|&x| {
+ | _____________^
+LL | | x < 0
+LL | | }
+LL | | ).is_none();
+ | |______________________________^
+ |
+ = help: this is more succinctly expressed by calling `any()` with negation
+
+error: aborting due to 6 previous errors
// run-rustfix
-
+#![allow(dead_code)]
#![warn(clippy::search_is_some)]
fn main() {
let _ = s1[2..].contains(&s2);
let _ = s1[2..].contains(&s2[2..]);
}
+
+fn is_none() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+ let y = &&42;
+
+ // Check `find().is_none()`, single-line case.
+ let _ = !v.iter().any(|x| *x < 0);
+ let _ = !(0..1).any(|x| **y == x); // one dereference less
+ let _ = !(0..1).any(|x| x == 0);
+ let _ = !v.iter().any(|x| *x == 0);
+
+ // Check `position().is_none()`, single-line case.
+ let _ = !v.iter().any(|&x| x < 0);
+
+ // Check `rposition().is_none()`, single-line case.
+ let _ = !v.iter().any(|&x| x < 0);
+
+ let s1 = String::from("hello world");
+ let s2 = String::from("world");
+
+ // caller of `find()` is a `&`static str`
+ let _ = !"hello world".contains("world");
+ let _ = !"hello world".contains(&s2);
+ let _ = !"hello world".contains(&s2[2..]);
+ // caller of `find()` is a `String`
+ let _ = !s1.contains("world");
+ let _ = !s1.contains(&s2);
+ let _ = !s1.contains(&s2[2..]);
+ // caller of `find()` is slice of `String`
+ let _ = !s1[2..].contains("world");
+ let _ = !s1[2..].contains(&s2);
+ let _ = !s1[2..].contains(&s2[2..]);
+}
// run-rustfix
-
+#![allow(dead_code)]
#![warn(clippy::search_is_some)]
fn main() {
let _ = s1[2..].find(&s2).is_some();
let _ = s1[2..].find(&s2[2..]).is_some();
}
+
+fn is_none() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+ let y = &&42;
+
+ // Check `find().is_none()`, single-line case.
+ let _ = v.iter().find(|&x| *x < 0).is_none();
+ let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+ let _ = (0..1).find(|x| *x == 0).is_none();
+ let _ = v.iter().find(|x| **x == 0).is_none();
+
+ // Check `position().is_none()`, single-line case.
+ let _ = v.iter().position(|&x| x < 0).is_none();
+
+ // Check `rposition().is_none()`, single-line case.
+ let _ = v.iter().rposition(|&x| x < 0).is_none();
+
+ let s1 = String::from("hello world");
+ let s2 = String::from("world");
+
+ // caller of `find()` is a `&`static str`
+ let _ = "hello world".find("world").is_none();
+ let _ = "hello world".find(&s2).is_none();
+ let _ = "hello world".find(&s2[2..]).is_none();
+ // caller of `find()` is a `String`
+ let _ = s1.find("world").is_none();
+ let _ = s1.find(&s2).is_none();
+ let _ = s1.find(&s2[2..]).is_none();
+ // caller of `find()` is slice of `String`
+ let _ = s1[2..].find("world").is_none();
+ let _ = s1[2..].find(&s2).is_none();
+ let _ = s1[2..].find(&s2[2..]).is_none();
+}
LL | let _ = s1[2..].find(&s2[2..]).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
-error: aborting due to 15 previous errors
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable.rs:42:13
+ |
+LL | let _ = v.iter().find(|&x| *x < 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable.rs:43:13
+ |
+LL | let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable.rs:44:13
+ |
+LL | let _ = (0..1).find(|x| *x == 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable.rs:45:13
+ |
+LL | let _ = v.iter().find(|x| **x == 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `position`
+ --> $DIR/search_is_some_fixable.rs:48:13
+ |
+LL | let _ = v.iter().position(|&x| x < 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after searching an `Iterator` with `rposition`
+ --> $DIR/search_is_some_fixable.rs:51:13
+ |
+LL | let _ = v.iter().rposition(|&x| x < 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable.rs:57:13
+ |
+LL | let _ = "hello world".find("world").is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable.rs:58:13
+ |
+LL | let _ = "hello world".find(&s2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable.rs:59:13
+ |
+LL | let _ = "hello world".find(&s2[2..]).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable.rs:61:13
+ |
+LL | let _ = s1.find("world").is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable.rs:62:13
+ |
+LL | let _ = s1.find(&s2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable.rs:63:13
+ |
+LL | let _ = s1.find(&s2[2..]).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable.rs:65:13
+ |
+LL | let _ = s1[2..].find("world").is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable.rs:66:13
+ |
+LL | let _ = s1[2..].find(&s2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable.rs:67:13
+ |
+LL | let _ = s1[2..].find(&s2[2..]).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])`
+
+error: aborting due to 30 previous errors
let bs = br###"raw string with 3# plus " ""###;
+ let bs = b"lit to string".to_vec();
+ let bs = b"lit to owned".to_vec();
+
// no warning, because these cannot be written as byte string literals:
let ubs = "☃".as_bytes();
let ubs = "hello there! this is a very long string".as_bytes();
+ let ubs = "☃".to_string().into_bytes();
+ let ubs = "this is also too long and shouldn't be fixed".to_string().into_bytes();
+
let strify = stringify!(foobar).as_bytes();
let current_version = env!("CARGO_PKG_VERSION").as_bytes();
let bs = r###"raw string with 3# plus " ""###.as_bytes();
+ let bs = "lit to string".to_string().into_bytes();
+ let bs = "lit to owned".to_owned().into_bytes();
+
// no warning, because these cannot be written as byte string literals:
let ubs = "☃".as_bytes();
let ubs = "hello there! this is a very long string".as_bytes();
+ let ubs = "☃".to_string().into_bytes();
+ let ubs = "this is also too long and shouldn't be fixed".to_string().into_bytes();
+
let strify = stringify!(foobar).as_bytes();
let current_version = env!("CARGO_PKG_VERSION").as_bytes();
LL | let bs = r###"raw string with 3# plus " ""###.as_bytes();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `br###"raw string with 3# plus " ""###`
+error: calling `into_bytes()` on a string literal
+ --> $DIR/string_lit_as_bytes.rs:11:14
+ |
+LL | let bs = "lit to string".to_string().into_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"lit to string".to_vec()`
+
+error: calling `into_bytes()` on a string literal
+ --> $DIR/string_lit_as_bytes.rs:12:14
+ |
+LL | let bs = "lit to owned".to_owned().into_bytes();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"lit to owned".to_vec()`
+
error: calling `as_bytes()` on `include_str!(..)`
- --> $DIR/string_lit_as_bytes.rs:19:22
+ --> $DIR/string_lit_as_bytes.rs:25:22
|
LL | let includestr = include_str!("entry_unfixable.rs").as_bytes();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")`
error: calling `as_bytes()` on a string literal
- --> $DIR/string_lit_as_bytes.rs:21:13
+ --> $DIR/string_lit_as_bytes.rs:27:13
|
LL | let _ = "string with newline/t/n".as_bytes();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"string with newline/t/n"`
-error: aborting due to 4 previous errors
+error: aborting due to 6 previous errors
fn main() {
let _ = (0..3).map(|x| x + 2).count();
+
+ let f = |x| x + 1;
+ let _ = (0..3).map(f).count();
+}
+
+fn negative() {
+ // closure with side effects
+ let mut sum = 0;
+ let _ = (0..3).map(|x| sum += x).count();
+
+ // closure variable with side effects
+ let ext_closure = |x| sum += x;
+ let _ = (0..3).map(ext_closure).count();
+
+ // closure that returns unit
+ let _ = (0..3)
+ .map(|x| {
+ // do nothing
+ })
+ .count();
+
+ // external function
+ let _ = (0..3).map(do_something).count();
+}
+
+fn do_something<T>(t: T) -> String {
+ unimplemented!()
}
= note: `-D clippy::suspicious-map` implied by `-D warnings`
= help: make sure you did not confuse `map` with `filter` or `for_each`
-error: aborting due to previous error
+error: this call to `map()` won't have an effect on the call to `count()`
+ --> $DIR/suspicious_map.rs:7:13
+ |
+LL | let _ = (0..3).map(f).count();
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: make sure you did not confuse `map` with `filter` or `for_each`
+
+error: aborting due to 2 previous errors
-error: casting to the same type is unnecessary (`i32` -> `i32`)
+error: casting integer literal to `i32` is unnecessary
--> $DIR/unnecessary_cast.rs:6:5
|
LL | 1i32 as i32;
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ help: try: `1_i32`
|
= note: `-D clippy::unnecessary-cast` implied by `-D warnings`
-error: casting to the same type is unnecessary (`f32` -> `f32`)
+error: casting float literal to `f32` is unnecessary
--> $DIR/unnecessary_cast.rs:7:5
|
LL | 1f32 as f32;
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ help: try: `1_f32`
error: casting to the same type is unnecessary (`bool` -> `bool`)
--> $DIR/unnecessary_cast.rs:8:5
|
LL | false as bool;
- | ^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^ help: try: `false`
error: aborting due to 3 previous errors
// `GccLlvmSomething`
struct GCCLLVMSomething;
+// public items must not be linted
+pub struct NOWARNINGHERE;
+pub struct ALSONoWarningHERE;
+
fn main() {}
mod issue2894 {
trait IntoBytes {
- fn to_bytes(&self) -> Vec<u8>;
+ fn to_bytes(self) -> Vec<u8>;
}
// This should not be linted
impl IntoBytes for u8 {
- fn to_bytes(&self) -> Vec<u8> {
- vec![*self]
+ fn to_bytes(self) -> Vec<u8> {
+ vec![self]
}
}
}
mod issue2894 {
trait IntoBytes {
- fn to_bytes(&self) -> Vec<u8>;
+ fn to_bytes(self) -> Vec<u8>;
}
// This should not be linted
impl IntoBytes for u8 {
- fn to_bytes(&self) -> Vec<u8> {
- vec![*self]
+ fn to_bytes(self) -> Vec<u8> {
+ vec![self]
}
}
}
let error_kind = ErrorKind::NotFound;
match error_kind {
ErrorKind::NotFound => {},
- std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _ => {},
+ ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _ => {},
}
match error_kind {
ErrorKind::NotFound => {},
-error: wildcard match will miss any future added variants
+error: wildcard match will also match any future added variants
--> $DIR/wildcard_enum_match_arm.rs:39:9
|
LL | _ => eprintln!("Not red"),
LL | #![deny(clippy::wildcard_enum_match_arm)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: wildcard match will miss any future added variants
+error: wildcard match will also match any future added variants
--> $DIR/wildcard_enum_match_arm.rs:43:9
|
LL | _not_red => eprintln!("Not red"),
| ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan`
-error: wildcard match will miss any future added variants
+error: wildcard match will also match any future added variants
--> $DIR/wildcard_enum_match_arm.rs:47:9
|
LL | not_red => format!("{:?}", not_red),
| ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan`
-error: wildcard match will miss any future added variants
+error: wildcard match will also match any future added variants
--> $DIR/wildcard_enum_match_arm.rs:63:9
|
LL | _ => "No red",
| ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan`
-error: match on non-exhaustive enum doesn't explicitly match all known variants
+error: wildcard matches known variants and will also match future added variants
--> $DIR/wildcard_enum_match_arm.rs:80:9
|
LL | _ => {},
- | ^ help: try this: `std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _`
+ | ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _`
error: aborting due to 5 previous errors
| ^^^^^^^
|
= note: `-D clippy::write-literal` implied by `-D warnings`
+help: try this
+ |
+LL | write!(&mut v, "Hello world");
+ | ^^^^^--
error: literal with an empty format string
--> $DIR/write_literal.rs:31:44
|
LL | writeln!(&mut v, "Hello {} {}", world, "world");
| ^^^^^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, "Hello {} world", world);
+ | ^^^^^ --
error: literal with an empty format string
--> $DIR/write_literal.rs:32:34
|
LL | writeln!(&mut v, "Hello {}", "world");
| ^^^^^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, "Hello world");
+ | ^^^^^--
error: literal with an empty format string
--> $DIR/write_literal.rs:37:33
|
LL | writeln!(&mut v, "{0} {1}", "hello", "world");
| ^^^^^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, "hello {1}", "world");
+ | ^^^^^ --
error: literal with an empty format string
--> $DIR/write_literal.rs:37:42
|
LL | writeln!(&mut v, "{0} {1}", "hello", "world");
| ^^^^^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, "{0} world", "hello");
+ | ^^^^^ --
error: literal with an empty format string
--> $DIR/write_literal.rs:38:33
|
LL | writeln!(&mut v, "{1} {0}", "hello", "world");
| ^^^^^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, "{1} hello", "world");
+ | ^^^^^--
error: literal with an empty format string
--> $DIR/write_literal.rs:38:42
|
LL | writeln!(&mut v, "{1} {0}", "hello", "world");
| ^^^^^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, "world {0}", "hello");
+ | ^^^^^ --
error: literal with an empty format string
- --> $DIR/write_literal.rs:41:43
+ --> $DIR/write_literal.rs:41:37
|
LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world");
- | ^^^^^^^
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, "hello {bar}", bar = "world");
+ | ^^^^^ --
error: literal with an empty format string
- --> $DIR/write_literal.rs:41:58
+ --> $DIR/write_literal.rs:41:52
|
LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world");
- | ^^^^^^^
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, "{foo} world", foo = "hello");
+ | ^^^^^ --
error: literal with an empty format string
- --> $DIR/write_literal.rs:42:43
+ --> $DIR/write_literal.rs:42:37
|
LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world");
- | ^^^^^^^
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, "{bar} hello", bar = "world");
+ | ^^^^^--
error: literal with an empty format string
- --> $DIR/write_literal.rs:42:58
+ --> $DIR/write_literal.rs:42:52
|
LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world");
- | ^^^^^^^
+ | ^^^^^^^^^^^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, "world {foo}", foo = "hello");
+ | ^^^^^ --
error: aborting due to 11 previous errors
--- /dev/null
+#![allow(unused_must_use)]
+#![warn(clippy::write_literal)]
+
+use std::io::Write;
+
+fn main() {
+ let mut v = Vec::new();
+
+ writeln!(&mut v, "{}", "{hello}");
+ writeln!(&mut v, r"{}", r"{hello}");
+ writeln!(&mut v, "{}", '\'');
+ writeln!(&mut v, "{}", '"');
+ writeln!(&mut v, r"{}", '"'); // don't lint
+ writeln!(&mut v, r"{}", '\'');
+ writeln!(
+ &mut v,
+ "some {}",
+ "hello \
+ world!"
+ );
+ writeln!(
+ &mut v,
+ "some {}\
+ {} \\ {}",
+ "1", "2", "3",
+ );
+}
--- /dev/null
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:9:28
+ |
+LL | writeln!(&mut v, "{}", "{hello}");
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::write-literal` implied by `-D warnings`
+help: try this
+ |
+LL | writeln!(&mut v, "{{hello}}");
+ | ^^^^^^^^^--
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:10:29
+ |
+LL | writeln!(&mut v, r"{}", r"{hello}");
+ | ^^^^^^^^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, r"{{hello}}");
+ | ^^^^^^^^^--
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:11:28
+ |
+LL | writeln!(&mut v, "{}", '/'');
+ | ^^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, "'");
+ | ^--
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:12:28
+ |
+LL | writeln!(&mut v, "{}", '"');
+ | ^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, "/"");
+ | ^^--
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:14:29
+ |
+LL | writeln!(&mut v, r"{}", '/'');
+ | ^^^^
+ |
+help: try this
+ |
+LL | writeln!(&mut v, r"'");
+ | ^--
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:18:9
+ |
+LL | / "hello /
+LL | | world!"
+ | |_______________^
+ |
+help: try this
+ |
+LL | "some hello /
+LL | world!"
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:25:9
+ |
+LL | "1", "2", "3",
+ | ^^^
+ |
+help: try this
+ |
+LL | "some 1{} / {}", "2", "3",
+ | ^ --
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:25:14
+ |
+LL | "1", "2", "3",
+ | ^^^
+ |
+help: try this
+ |
+LL | 2 / {}",
+LL | "1", "3",
+ |
+
+error: literal with an empty format string
+ --> $DIR/write_literal_2.rs:25:19
+ |
+LL | "1", "2", "3",
+ | ^^^
+ |
+help: try this
+ |
+LL | {} / 3",
+LL | "1", "2",
+ |
+
+error: aborting due to 9 previous errors
+
fn to_mut(&mut self);
}
}
+
+mod issue6727 {
+ trait ToU64 {
+ fn to_u64(self) -> u64;
+ fn to_u64_v2(&self) -> u64;
+ }
+
+ #[derive(Clone, Copy)]
+ struct FooCopy;
+
+ impl ToU64 for FooCopy {
+ fn to_u64(self) -> u64 {
+ 1
+ }
+ // trigger lint
+ fn to_u64_v2(&self) -> u64 {
+ 1
+ }
+ }
+
+ struct FooNoCopy;
+
+ impl ToU64 for FooNoCopy {
+ // trigger lint
+ fn to_u64(self) -> u64 {
+ 2
+ }
+ fn to_u64_v2(&self) -> u64 {
+ 2
+ }
+ }
+}
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
--> $DIR/wrong_self_convention.rs:18:17
|
LL | fn from_i32(self) {}
| ^^^^
|
= note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+ = help: consider choosing a less ambiguous name
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
--> $DIR/wrong_self_convention.rs:24:21
|
LL | pub fn from_i64(self) {}
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
--> $DIR/wrong_self_convention.rs:36:15
|
LL | fn as_i32(self) {}
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
+error: methods called `into_*` usually take `self` by value
--> $DIR/wrong_self_convention.rs:38:17
|
LL | fn into_i32(&self) {}
| ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
+error: methods called `is_*` usually take `self` by reference or no `self`
--> $DIR/wrong_self_convention.rs:40:15
|
LL | fn is_i32(self) {}
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
--> $DIR/wrong_self_convention.rs:42:15
|
LL | fn to_i32(self) {}
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
--> $DIR/wrong_self_convention.rs:44:17
|
LL | fn from_i32(self) {}
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
--> $DIR/wrong_self_convention.rs:46:19
|
LL | pub fn as_i64(self) {}
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
+error: methods called `into_*` usually take `self` by value
--> $DIR/wrong_self_convention.rs:47:21
|
LL | pub fn into_i64(&self) {}
| ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
+error: methods called `is_*` usually take `self` by reference or no `self`
--> $DIR/wrong_self_convention.rs:48:19
|
LL | pub fn is_i64(self) {}
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
--> $DIR/wrong_self_convention.rs:49:19
|
LL | pub fn to_i64(self) {}
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
--> $DIR/wrong_self_convention.rs:50:21
|
LL | pub fn from_i64(self) {}
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
--> $DIR/wrong_self_convention.rs:95:19
|
LL | fn as_i32(self) {}
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
+error: methods called `into_*` usually take `self` by value
--> $DIR/wrong_self_convention.rs:98:25
|
LL | fn into_i32_ref(&self) {}
| ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
+error: methods called `is_*` usually take `self` by reference or no `self`
--> $DIR/wrong_self_convention.rs:100:19
|
LL | fn is_i32(self) {}
| ^^^^
-
-error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
- --> $DIR/wrong_self_convention.rs:102:19
|
-LL | fn to_i32(self) {}
- | ^^^^
+ = help: consider choosing a less ambiguous name
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
--> $DIR/wrong_self_convention.rs:104:21
|
LL | fn from_i32(self) {}
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
--> $DIR/wrong_self_convention.rs:119:19
|
LL | fn as_i32(self);
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
+error: methods called `into_*` usually take `self` by value
--> $DIR/wrong_self_convention.rs:122:25
|
LL | fn into_i32_ref(&self);
| ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
+error: methods called `is_*` usually take `self` by reference or no `self`
--> $DIR/wrong_self_convention.rs:124:19
|
LL | fn is_i32(self);
| ^^^^
-
-error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
- --> $DIR/wrong_self_convention.rs:126:19
|
-LL | fn to_i32(self);
- | ^^^^
+ = help: consider choosing a less ambiguous name
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
--> $DIR/wrong_self_convention.rs:128:21
|
LL | fn from_i32(self);
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
+error: methods called `into_*` usually take `self` by value
--> $DIR/wrong_self_convention.rs:146:25
|
LL | fn into_i32_ref(&self);
| ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
--> $DIR/wrong_self_convention.rs:152:21
|
LL | fn from_i32(self);
| ^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value
+ --> $DIR/wrong_self_convention.rs:181:22
+ |
+LL | fn to_u64_v2(&self) -> u64 {
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
+ --> $DIR/wrong_self_convention.rs:190:19
+ |
+LL | fn to_u64(self) -> u64 {
+ | ^^^^
+ |
+ = help: consider choosing a less ambiguous name
error: aborting due to 24 previous errors
--- /dev/null
+// edition:2018
+#![warn(clippy::wrong_self_convention)]
+#![allow(dead_code)]
+
+fn main() {}
+
+mod issue6758 {
+ pub enum Test<T> {
+ One(T),
+ Many(Vec<T>),
+ }
+
+ impl<T> Test<T> {
+ // If a method starts with `to_` and not ends with `_mut` it should expect `&self`
+ pub fn to_many(&mut self) -> Option<&mut [T]> {
+ match self {
+ Self::Many(data) => Some(data),
+ _ => None,
+ }
+ }
+
+ // If a method starts with `to_` and ends with `_mut` it should expect `&mut self`
+ pub fn to_many_mut(&self) -> Option<&[T]> {
+ match self {
+ Self::Many(data) => Some(data),
+ _ => None,
+ }
+ }
+ }
+}
--- /dev/null
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
+ --> $DIR/wrong_self_conventions_mut.rs:15:24
+ |
+LL | pub fn to_many(&mut self) -> Option<&mut [T]> {
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+ = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `*_mut`) usually take `self` by mutable reference
+ --> $DIR/wrong_self_conventions_mut.rs:23:28
+ |
+LL | pub fn to_many_mut(&self) -> Option<&[T]> {
+ | ^^^^^
+ |
+ = help: consider choosing a less ambiguous name
+
+error: aborting due to 2 previous errors
+