mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-10 07:04:18 +00:00
Merge commit '66c29b973b3b10278bd39f4e26b08522a379c2c9' into clippy-subtree-update
This commit is contained in:
parent
0b6cf3b78c
commit
798865c593
158 changed files with 2852 additions and 1012 deletions
|
@ -5606,6 +5606,7 @@ Released 2018-09-13
|
|||
[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
|
||||
[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
|
||||
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
|
||||
[`suspicious_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_open_options
|
||||
[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
|
||||
[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
|
||||
[`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned
|
||||
|
@ -5814,6 +5815,7 @@ Released 2018-09-13
|
|||
[`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments
|
||||
[`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates
|
||||
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
|
||||
[`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates
|
||||
[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow
|
||||
[`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
|
||||
[`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior
|
||||
|
|
|
@ -212,7 +212,7 @@ default configuration of Clippy. By default, any configuration will replace the
|
|||
* `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
|
||||
* `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
|
||||
|
||||
**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "WebGL2", "WebGPU", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
|
||||
**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
|
@ -768,7 +768,19 @@ Additional dotfiles (files or directories starting with a dot) to allow
|
|||
* [`path_ends_with_ext`](https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext)
|
||||
|
||||
|
||||
## `allowed-duplicate-crates`
|
||||
A list of crate names to allow duplicates of
|
||||
|
||||
**Default Value:** `[]`
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`multiple_crate_versions`](https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions)
|
||||
|
||||
|
||||
## `enforce-iter-loop-reborrow`
|
||||
Whether to recommend using implicit into iter for reborrowed values.
|
||||
|
||||
#### Example
|
||||
```no_run
|
||||
let mut vec = vec![1, 2, 3];
|
||||
|
@ -793,7 +805,7 @@ for _ in &mut *rmvec {}
|
|||
|
||||
|
||||
## `check-private-items`
|
||||
|
||||
Whether to also run the listed lints on private items.
|
||||
|
||||
**Default Value:** `false`
|
||||
|
||||
|
@ -806,9 +818,10 @@ for _ in &mut *rmvec {}
|
|||
|
||||
|
||||
## `pub-underscore-fields-behavior`
|
||||
Lint "public" fields in a struct that are prefixed with an underscore based on their
|
||||
exported visibility, or whether they are marked as "pub".
|
||||
|
||||
|
||||
**Default Value:** `"PublicallyExported"`
|
||||
**Default Value:** `"PubliclyExported"`
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
|
|
|
@ -2,7 +2,9 @@ use crate::msrvs::Msrv;
|
|||
use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename};
|
||||
use crate::ClippyConfiguration;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::edit_distance::edit_distance;
|
||||
use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext};
|
||||
use serde::de::{IgnoredAny, IntoDeserializer, MapAccess, Visitor};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
@ -26,7 +28,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
|
|||
"NaN", "NaNs",
|
||||
"OAuth", "GraphQL",
|
||||
"OCaml",
|
||||
"OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
|
||||
"OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry",
|
||||
"WebGL", "WebGL2", "WebGPU",
|
||||
"TensorFlow",
|
||||
"TrueType",
|
||||
|
@ -59,18 +61,25 @@ impl TryConf {
|
|||
#[derive(Debug)]
|
||||
struct ConfError {
|
||||
message: String,
|
||||
suggestion: Option<Suggestion>,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl ConfError {
|
||||
fn from_toml(file: &SourceFile, error: &toml::de::Error) -> Self {
|
||||
let span = error.span().unwrap_or(0..file.source_len.0 as usize);
|
||||
Self::spanned(file, error.message(), span)
|
||||
Self::spanned(file, error.message(), None, span)
|
||||
}
|
||||
|
||||
fn spanned(file: &SourceFile, message: impl Into<String>, span: Range<usize>) -> Self {
|
||||
fn spanned(
|
||||
file: &SourceFile,
|
||||
message: impl Into<String>,
|
||||
suggestion: Option<Suggestion>,
|
||||
span: Range<usize>,
|
||||
) -> Self {
|
||||
Self {
|
||||
message: message.into(),
|
||||
suggestion,
|
||||
span: Span::new(
|
||||
file.start_pos + BytePos::from_usize(span.start),
|
||||
file.start_pos + BytePos::from_usize(span.end),
|
||||
|
@ -147,16 +156,18 @@ macro_rules! define_Conf {
|
|||
match Field::deserialize(name.get_ref().as_str().into_deserializer()) {
|
||||
Err(e) => {
|
||||
let e: FieldError = e;
|
||||
errors.push(ConfError::spanned(self.0, e.0, name.span()));
|
||||
errors.push(ConfError::spanned(self.0, e.error, e.suggestion, name.span()));
|
||||
}
|
||||
$(Ok(Field::$name) => {
|
||||
$(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), name.span()));)?
|
||||
$(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)?
|
||||
let raw_value = map.next_value::<toml::Spanned<toml::Value>>()?;
|
||||
let value_span = raw_value.span();
|
||||
match <$ty>::deserialize(raw_value.into_inner()) {
|
||||
Err(e) => errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), value_span)),
|
||||
Err(e) => errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), None, value_span)),
|
||||
Ok(value) => match $name {
|
||||
Some(_) => errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), name.span())),
|
||||
Some(_) => {
|
||||
errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span()));
|
||||
}
|
||||
None => {
|
||||
$name = Some(value);
|
||||
// $new_conf is the same as one of the defined `$name`s, so
|
||||
|
@ -165,7 +176,7 @@ macro_rules! define_Conf {
|
|||
Some(_) => errors.push(ConfError::spanned(self.0, concat!(
|
||||
"duplicate field `", stringify!($new_conf),
|
||||
"` (provided as `", stringify!($name), "`)"
|
||||
), name.span())),
|
||||
), None, name.span())),
|
||||
None => $new_conf = $name.clone(),
|
||||
})?
|
||||
},
|
||||
|
@ -523,7 +534,11 @@ define_Conf! {
|
|||
///
|
||||
/// Additional dotfiles (files or directories starting with a dot) to allow
|
||||
(allowed_dotfiles: FxHashSet<String> = FxHashSet::default()),
|
||||
/// Lint: EXPLICIT_ITER_LOOP
|
||||
/// Lint: MULTIPLE_CRATE_VERSIONS.
|
||||
///
|
||||
/// A list of crate names to allow duplicates of
|
||||
(allowed_duplicate_crates: FxHashSet<String> = FxHashSet::default()),
|
||||
/// Lint: EXPLICIT_ITER_LOOP.
|
||||
///
|
||||
/// Whether to recommend using implicit into iter for reborrowed values.
|
||||
///
|
||||
|
@ -543,15 +558,15 @@ define_Conf! {
|
|||
/// for _ in &mut *rmvec {}
|
||||
/// ```
|
||||
(enforce_iter_loop_reborrow: bool = false),
|
||||
/// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC
|
||||
/// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC.
|
||||
///
|
||||
/// Whether to also run the listed lints on private items.
|
||||
(check_private_items: bool = false),
|
||||
/// Lint: PUB_UNDERSCORE_FIELDS
|
||||
/// Lint: PUB_UNDERSCORE_FIELDS.
|
||||
///
|
||||
/// Lint "public" fields in a struct that are prefixed with an underscore based on their
|
||||
/// exported visibility, or whether they are marked as "pub".
|
||||
(pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PublicallyExported),
|
||||
(pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported),
|
||||
}
|
||||
|
||||
/// Search for the configuration file.
|
||||
|
@ -669,10 +684,16 @@ impl Conf {
|
|||
|
||||
// all conf errors are non-fatal, we just use the default conf in case of error
|
||||
for error in errors {
|
||||
sess.dcx().span_err(
|
||||
let mut diag = sess.dcx().struct_span_err(
|
||||
error.span,
|
||||
format!("error reading Clippy's configuration file: {}", error.message),
|
||||
);
|
||||
|
||||
if let Some(sugg) = error.suggestion {
|
||||
diag.span_suggestion(error.span, sugg.message, sugg.suggestion, Applicability::MaybeIncorrect);
|
||||
}
|
||||
|
||||
diag.emit();
|
||||
}
|
||||
|
||||
for warning in warnings {
|
||||
|
@ -689,19 +710,31 @@ impl Conf {
|
|||
const SEPARATOR_WIDTH: usize = 4;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FieldError(String);
|
||||
struct FieldError {
|
||||
error: String,
|
||||
suggestion: Option<Suggestion>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Suggestion {
|
||||
message: &'static str,
|
||||
suggestion: &'static str,
|
||||
}
|
||||
|
||||
impl std::error::Error for FieldError {}
|
||||
|
||||
impl Display for FieldError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.pad(&self.0)
|
||||
f.pad(&self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::de::Error for FieldError {
|
||||
fn custom<T: Display>(msg: T) -> Self {
|
||||
Self(msg.to_string())
|
||||
Self {
|
||||
error: msg.to_string(),
|
||||
suggestion: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self {
|
||||
|
@ -723,7 +756,20 @@ impl serde::de::Error for FieldError {
|
|||
write!(msg, "{:SEPARATOR_WIDTH$}{field:column_width$}", " ").unwrap();
|
||||
}
|
||||
}
|
||||
Self(msg)
|
||||
|
||||
let suggestion = expected
|
||||
.iter()
|
||||
.filter_map(|expected| {
|
||||
let dist = edit_distance(field, expected, 4)?;
|
||||
Some((dist, expected))
|
||||
})
|
||||
.min_by_key(|&(dist, _)| dist)
|
||||
.map(|(_, suggestion)| Suggestion {
|
||||
message: "perhaps you meant",
|
||||
suggestion,
|
||||
});
|
||||
|
||||
Self { error: msg, suggestion }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ extern crate rustc_ast;
|
|||
extern crate rustc_data_structures;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate rustc_driver;
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_session;
|
||||
extern crate rustc_span;
|
||||
|
||||
|
|
|
@ -129,6 +129,6 @@ unimplemented_serialize! {
|
|||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub enum PubUnderscoreFieldsBehaviour {
|
||||
PublicallyExported,
|
||||
PubliclyExported,
|
||||
AllPubFields,
|
||||
}
|
||||
|
|
|
@ -4,11 +4,11 @@ version = "0.0.1"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
aho-corasick = "0.7"
|
||||
aho-corasick = "1.0"
|
||||
clap = "4.1.4"
|
||||
indoc = "1.0"
|
||||
itertools = "0.11"
|
||||
opener = "0.5"
|
||||
opener = "0.6"
|
||||
shell-escape = "0.1"
|
||||
walkdir = "2.3"
|
||||
|
||||
|
|
|
@ -504,9 +504,8 @@ fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<S
|
|||
}
|
||||
|
||||
let searcher = AhoCorasickBuilder::new()
|
||||
.dfa(true)
|
||||
.match_kind(aho_corasick::MatchKind::LeftmostLongest)
|
||||
.build_with_size::<u16, _, _>(replacements.iter().map(|&(x, _)| x.as_bytes()))
|
||||
.build(replacements.iter().map(|&(x, _)| x.as_bytes()))
|
||||
.unwrap();
|
||||
|
||||
let mut result = String::with_capacity(contents.len() + 1024);
|
||||
|
|
|
@ -10,7 +10,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
arrayvec = { version = "0.7", default-features = false }
|
||||
cargo_metadata = "0.15.3"
|
||||
cargo_metadata = "0.18"
|
||||
clippy_config = { path = "../clippy_config" }
|
||||
clippy_utils = { path = "../clippy_utils" }
|
||||
declare_clippy_lint = { path = "../declare_clippy_lint" }
|
||||
|
|
|
@ -14,10 +14,10 @@ declare_clippy_lint! {
|
|||
/// This lint warns when you use `Arc` with a type that does not implement `Send` or `Sync`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `Arc<T>` is an Atomic `RC<T>` and guarantees that updates to the reference counter are
|
||||
/// Atomic. This is useful in multiprocessing scenarios. To send an `Arc<T>` across processes
|
||||
/// and make use of the atomic ref counter, `T` must be [both `Send` and `Sync`](https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-Send-for-Arc%3CT%3E),
|
||||
/// either `T` should be made `Send + Sync` or an `Rc` should be used instead of an `Arc`
|
||||
/// `Arc<T>` is a thread-safe `Rc<T>` and guarantees that updates to the reference counter
|
||||
/// use atomic operations. To send an `Arc<T>` across thread boundaries and
|
||||
/// share ownership between multiple threads, `T` must be [both `Send` and `Sync`](https://doc.rust-lang.org/std/sync/struct.Arc.html#thread-safety),
|
||||
/// so either `T` should be made `Send + Sync` or an `Rc` should be used instead of an `Arc`
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
|
|
|
@ -67,6 +67,11 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions {
|
|||
);
|
||||
|
||||
if let ExprKind::Block(block, _) = &cond.kind {
|
||||
if !block.span.eq_ctxt(expr.span) {
|
||||
// If the block comes from a macro, or as an argument to a macro,
|
||||
// do not lint.
|
||||
return;
|
||||
}
|
||||
if block.rules == BlockCheckMode::DefaultBlock {
|
||||
if block.stmts.is_empty() {
|
||||
if let Some(ex) = &block.expr {
|
||||
|
|
|
@ -6,6 +6,7 @@ mod wildcard_dependencies;
|
|||
use cargo_metadata::MetadataCommand;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_lint_allowed;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::hir_id::CRATE_HIR_ID;
|
||||
use rustc_lint::{LateContext, LateLintPass, Lint};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
@ -128,6 +129,8 @@ declare_clippy_lint! {
|
|||
/// ### Known problems
|
||||
/// Because this can be caused purely by the dependencies
|
||||
/// themselves, it's not always possible to fix this issue.
|
||||
/// In those cases, you can allow that specific crate using
|
||||
/// the `allowed_duplicate_crates` configuration option.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
|
@ -163,6 +166,7 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct Cargo {
|
||||
pub allowed_duplicate_crates: FxHashSet<String>,
|
||||
pub ignore_publish: bool,
|
||||
}
|
||||
|
||||
|
@ -208,7 +212,7 @@ impl LateLintPass<'_> for Cargo {
|
|||
{
|
||||
match MetadataCommand::new().exec() {
|
||||
Ok(metadata) => {
|
||||
multiple_crate_versions::check(cx, &metadata);
|
||||
multiple_crate_versions::check(cx, &metadata, &self.allowed_duplicate_crates);
|
||||
},
|
||||
Err(e) => {
|
||||
for lint in WITH_DEPS_LINTS {
|
||||
|
|
|
@ -3,27 +3,40 @@
|
|||
use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use itertools::Itertools;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
use super::MULTIPLE_CRATE_VERSIONS;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, allowed_duplicate_crates: &FxHashSet<String>) {
|
||||
let local_name = cx.tcx.crate_name(LOCAL_CRATE);
|
||||
let mut packages = metadata.packages.clone();
|
||||
packages.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
|
||||
if let Some(resolve) = &metadata.resolve
|
||||
&& let Some(local_id) = packages.iter().find_map(|p| {
|
||||
if p.name == local_name.as_str() {
|
||||
// p.name contains the original crate names with dashes intact
|
||||
// local_name contains the crate name as a namespace, with the dashes converted to underscores
|
||||
// the code below temporarily rectifies this discrepancy
|
||||
if p.name
|
||||
.as_bytes()
|
||||
.iter()
|
||||
.map(|b| if b == &b'-' { &b'_' } else { b })
|
||||
.eq(local_name.as_str().as_bytes())
|
||||
{
|
||||
Some(&p.id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
{
|
||||
for (name, group) in &packages.iter().group_by(|p| p.name.clone()) {
|
||||
for (name, group) in &packages
|
||||
.iter()
|
||||
.filter(|p| !allowed_duplicate_crates.contains(&p.name))
|
||||
.group_by(|p| &p.name)
|
||||
{
|
||||
let group: Vec<&Package> = group.collect();
|
||||
|
||||
if group.len() <= 1 {
|
||||
|
|
|
@ -439,6 +439,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::methods::STR_SPLIT_AT_NEWLINE_INFO,
|
||||
crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO,
|
||||
crate::methods::SUSPICIOUS_MAP_INFO,
|
||||
crate::methods::SUSPICIOUS_OPEN_OPTIONS_INFO,
|
||||
crate::methods::SUSPICIOUS_SPLITN_INFO,
|
||||
crate::methods::SUSPICIOUS_TO_OWNED_INFO,
|
||||
crate::methods::TYPE_ID_ON_BOX_INFO,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::last_path_segment;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::{last_path_segment, std_or_core};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -42,12 +42,14 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
|
|||
&& ty.span.ctxt() == ctxt
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let sugg = make_sugg(cx, ty_path, ctxt, &mut applicability);
|
||||
let Some(path) = std_or_core(cx) else { return };
|
||||
let path = format!("{path}::iter::empty");
|
||||
let sugg = make_sugg(cx, ty_path, ctxt, &mut applicability, &path);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DEFAULT_INSTEAD_OF_ITER_EMPTY,
|
||||
expr.span,
|
||||
"`std::iter::empty()` is the more idiomatic way",
|
||||
&format!("`{path}()` is the more idiomatic way"),
|
||||
"try",
|
||||
sugg,
|
||||
applicability,
|
||||
|
@ -61,6 +63,7 @@ fn make_sugg(
|
|||
ty_path: &rustc_hir::QPath<'_>,
|
||||
ctxt: SyntaxContext,
|
||||
applicability: &mut Applicability,
|
||||
path: &str,
|
||||
) -> String {
|
||||
if let Some(last) = last_path_segment(ty_path).args
|
||||
&& let Some(iter_ty) = last.args.iter().find_map(|arg| match arg {
|
||||
|
@ -69,10 +72,10 @@ fn make_sugg(
|
|||
})
|
||||
{
|
||||
format!(
|
||||
"std::iter::empty::<{}>()",
|
||||
"{path}::<{}>()",
|
||||
snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0
|
||||
)
|
||||
} else {
|
||||
"std::iter::empty()".to_owned()
|
||||
format!("{path}()")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::numeric_literal;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{get_parent_node, numeric_literal};
|
||||
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
||||
use rustc_hir::{Block, Body, Expr, ExprKind, FnRetTy, HirId, ItemKind, Lit, Node, Stmt, StmtKind};
|
||||
use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty};
|
||||
|
@ -50,11 +50,11 @@ declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback {
|
||||
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
|
||||
let is_parent_const = if let Some(Node::Item(item)) = get_parent_node(cx.tcx, body.id().hir_id) {
|
||||
matches!(item.kind, ItemKind::Const(..))
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let hir = cx.tcx.hir();
|
||||
let is_parent_const = matches!(
|
||||
hir.body_const_context(hir.body_owner_def_id(body.id())),
|
||||
Some(ConstContext::Const { inline: false } | ConstContext::Static(_))
|
||||
);
|
||||
let mut visitor = NumericFallbackVisitor::new(cx, is_parent_const);
|
||||
visitor.visit_body(body);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
|
||||
use clippy_utils::{is_lint_allowed, match_def_path, paths};
|
||||
use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
|
||||
|
@ -450,6 +450,7 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r
|
|||
&& let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq)
|
||||
&& let Some(def_id) = trait_ref.trait_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::PartialEq, def_id)
|
||||
&& !has_non_exhaustive_attr(cx.tcx, *adt)
|
||||
&& let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id)
|
||||
&& !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[])
|
||||
// If all of our fields implement `Eq`, we can implement `Eq` too
|
||||
|
|
|
@ -6,7 +6,7 @@ use clippy_utils::diagnostics::span_lint;
|
|||
use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::emitter::HumanEmitter;
|
||||
use rustc_errors::DiagCtxt;
|
||||
use rustc_errors::{DiagCtxt, DiagnosticBuilder};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_parse::maybe_new_parser_from_source_str;
|
||||
use rustc_parse::parser::ForceCollect;
|
||||
|
@ -53,7 +53,7 @@ pub fn check(
|
|||
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
|
||||
Ok(p) => p,
|
||||
Err(errs) => {
|
||||
errs.into_iter().for_each(|err| err.cancel());
|
||||
errs.into_iter().for_each(DiagnosticBuilder::cancel);
|
||||
return (false, test_attr_spans);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -6,8 +6,8 @@ use clippy_utils::source::snippet_opt;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_path, Visitor};
|
||||
use rustc_hir::{
|
||||
GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, PathSegment, Ty,
|
||||
TyKind,
|
||||
FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path,
|
||||
PathSegment, Ty, TyKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter::OnlyBodies;
|
||||
|
@ -181,6 +181,9 @@ fn convert_to_from(
|
|||
let from = snippet_opt(cx, self_ty.span)?;
|
||||
let into = snippet_opt(cx, target_ty.span)?;
|
||||
|
||||
let return_type = matches!(sig.decl.output, FnRetTy::Return(_))
|
||||
.then_some(String::from("Self"))
|
||||
.unwrap_or_default();
|
||||
let mut suggestions = vec![
|
||||
// impl Into<T> for U -> impl From<T> for U
|
||||
// ~~~~ ~~~~
|
||||
|
@ -199,7 +202,7 @@ fn convert_to_from(
|
|||
(self_ident.span, format!("val: {from}")),
|
||||
// fn into(self) -> T -> fn into(self) -> Self
|
||||
// ~ ~~~~
|
||||
(sig.decl.output.span(), String::from("Self")),
|
||||
(sig.decl.output.span(), return_type),
|
||||
];
|
||||
|
||||
let mut finder = SelfFinder {
|
||||
|
|
|
@ -53,7 +53,9 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
|
|||
// List of spans to lint. (lint_span, first_span)
|
||||
let mut lint_spans = Vec::new();
|
||||
|
||||
let Ok(impls) = cx.tcx.crate_inherent_impls(()) else { return };
|
||||
let Ok(impls) = cx.tcx.crate_inherent_impls(()) else {
|
||||
return;
|
||||
};
|
||||
let inherent_impls = cx
|
||||
.tcx
|
||||
.with_stable_hashing_context(|hcx| impls.inherent_impls.to_sorted(&hcx, true));
|
||||
|
|
|
@ -574,6 +574,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
warn_on_all_wildcard_imports,
|
||||
check_private_items,
|
||||
pub_underscore_fields_behavior,
|
||||
ref allowed_duplicate_crates,
|
||||
|
||||
blacklisted_names: _,
|
||||
cyclomatic_complexity_threshold: _,
|
||||
|
@ -719,7 +720,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(|_| Box::new(needless_update::NeedlessUpdate));
|
||||
store.register_late_pass(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
|
||||
store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef));
|
||||
store.register_late_pass(|_| Box::new(no_effect::NoEffect));
|
||||
store.register_late_pass(|_| Box::<no_effect::NoEffect>::default());
|
||||
store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment));
|
||||
store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv())));
|
||||
store.register_late_pass(move |_| {
|
||||
|
@ -947,6 +948,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(move |_| {
|
||||
Box::new(cargo::Cargo {
|
||||
ignore_publish: cargo_ignore_publish,
|
||||
allowed_duplicate_crates: allowed_duplicate_crates.clone(),
|
||||
})
|
||||
});
|
||||
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
|
||||
|
|
|
@ -31,7 +31,7 @@ pub(super) fn check<'tcx>(
|
|||
vec.span,
|
||||
"it looks like the same item is being pushed into this Vec",
|
||||
None,
|
||||
&format!("try using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"),
|
||||
&format!("consider using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lin
|
|||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_non_aggregate_primitive_type;
|
||||
use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators};
|
||||
use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators, std_or_core};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
|
@ -128,6 +128,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
|
|||
// check if replacement is mem::MaybeUninit::uninit().assume_init()
|
||||
&& cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id)
|
||||
{
|
||||
let Some(top_crate) = std_or_core(cx) else { return };
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -136,7 +137,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
|
|||
"replacing with `mem::MaybeUninit::uninit().assume_init()`",
|
||||
"consider using",
|
||||
format!(
|
||||
"std::ptr::read({})",
|
||||
"{top_crate}::ptr::read({})",
|
||||
snippet_with_applicability(cx, dest.span, "", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
|
@ -149,6 +150,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
|
|||
&& let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
|
||||
{
|
||||
if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) {
|
||||
let Some(top_crate) = std_or_core(cx) else { return };
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -157,7 +159,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
|
|||
"replacing with `mem::uninitialized()`",
|
||||
"consider using",
|
||||
format!(
|
||||
"std::ptr::read({})",
|
||||
"{top_crate}::ptr::read({})",
|
||||
snippet_with_applicability(cx, dest.span, "", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
|
@ -184,14 +186,17 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
|
|||
return;
|
||||
}
|
||||
if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) {
|
||||
let Some(top_crate) = std_or_core(cx) else { return };
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MEM_REPLACE_WITH_DEFAULT,
|
||||
expr_span,
|
||||
"replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
|
||||
&format!(
|
||||
"replacing a value of type `T` with `T::default()` is better expressed using `{top_crate}::mem::take`"
|
||||
),
|
||||
|diag| {
|
||||
if !expr_span.from_expansion() {
|
||||
let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
|
||||
let suggestion = format!("{top_crate}::mem::take({})", snippet(cx, dest.span, ""));
|
||||
|
||||
diag.span_suggestion(
|
||||
expr_span,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{get_expr_use_or_unification_node, is_no_std_crate, is_res_lang_ctor, path_res};
|
||||
use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core};
|
||||
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
|
@ -58,10 +58,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re
|
|||
return;
|
||||
}
|
||||
|
||||
let Some(top_crate) = std_or_core(cx) else { return };
|
||||
if let Some(i) = item {
|
||||
let sugg = format!(
|
||||
"{}::iter::once({}{})",
|
||||
if is_no_std_crate(cx) { "core" } else { "std" },
|
||||
"{top_crate}::iter::once({}{})",
|
||||
iter_type.ref_prefix(),
|
||||
snippet(cx, i.span, "...")
|
||||
);
|
||||
|
@ -81,11 +81,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re
|
|||
expr.span,
|
||||
&format!("`{method_name}` call on an empty collection"),
|
||||
"try",
|
||||
if is_no_std_crate(cx) {
|
||||
"core::iter::empty()".to_string()
|
||||
} else {
|
||||
"std::iter::empty()".to_string()
|
||||
},
|
||||
format!("{top_crate}::iter::empty()"),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_a
|
|||
)
|
||||
.span_suggestion(
|
||||
expr_span,
|
||||
"if this is intentional, try using `Path::new` instead",
|
||||
"if this is intentional, consider using `Path::new`",
|
||||
format!("PathBuf::from({arg_str})"),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
|
|
|
@ -50,7 +50,7 @@ pub fn check(
|
|||
super::MANUAL_SATURATING_ARITHMETIC,
|
||||
expr.span,
|
||||
"manual saturating arithmetic",
|
||||
&format!("try using `saturating_{arith}`"),
|
||||
&format!("consider using `saturating_{arith}`"),
|
||||
format!(
|
||||
"{}.saturating_{arith}({})",
|
||||
snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
|
||||
|
|
|
@ -113,9 +113,15 @@ fn handle_path(
|
|||
if let Some(path_def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id()
|
||||
&& match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD)
|
||||
{
|
||||
// FIXME: It would be better to infer the type to check if it's copyable or not
|
||||
// to suggest to use `.copied()` instead of `.cloned()` where applicable.
|
||||
lint_path(cx, e.span, recv.span);
|
||||
// The `copied` and `cloned` methods are only available on `&T` and `&mut T` in `Option`
|
||||
// and `Result`.
|
||||
if let ty::Adt(_, args) = cx.typeck_results().expr_ty(recv).kind()
|
||||
&& let args = args.as_slice()
|
||||
&& let Some(ty) = args.iter().find_map(|generic_arg| generic_arg.as_type())
|
||||
&& ty.is_ref()
|
||||
{
|
||||
lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,17 +145,19 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
|
|||
);
|
||||
}
|
||||
|
||||
fn lint_path(cx: &LateContext<'_>, replace: Span, root: Span) {
|
||||
fn lint_path(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
let replacement = if is_copy { "copied" } else { "cloned" };
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_CLONE,
|
||||
replace,
|
||||
"you are explicitly cloning with `.map()`",
|
||||
"consider calling the dedicated `cloned` method",
|
||||
&format!("consider calling the dedicated `{replacement}` method"),
|
||||
format!(
|
||||
"{}.cloned()",
|
||||
"{}.{replacement}()",
|
||||
snippet_with_applicability(cx, root, "..", &mut applicability),
|
||||
),
|
||||
applicability,
|
||||
|
|
|
@ -2827,6 +2827,44 @@ declare_clippy_lint! {
|
|||
"nonsensical combination of options for opening a file"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the suspicious use of `OpenOptions::create()`
|
||||
/// without an explicit `OpenOptions::truncate()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `create()` alone will either create a new file or open an
|
||||
/// existing file. If the file already exists, it will be
|
||||
/// overwritten when written to, but the file will not be
|
||||
/// truncated by default.
|
||||
/// If less data is written to the file
|
||||
/// than it already contains, the remainder of the file will
|
||||
/// remain unchanged, and the end of the file will contain old
|
||||
/// data.
|
||||
/// In most cases, one should either use `create_new` to ensure
|
||||
/// the file is created from scratch, or ensure `truncate` is
|
||||
/// called so that the truncation behaviour is explicit. `truncate(true)`
|
||||
/// will ensure the file is entirely overwritten with new data, whereas
|
||||
/// `truncate(false)` will explicitely keep the default behavior.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,no_run
|
||||
/// use std::fs::OpenOptions;
|
||||
///
|
||||
/// OpenOptions::new().create(true);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,no_run
|
||||
/// use std::fs::OpenOptions;
|
||||
///
|
||||
/// OpenOptions::new().create(true).truncate(true);
|
||||
/// ```
|
||||
#[clippy::version = "1.75.0"]
|
||||
pub SUSPICIOUS_OPEN_OPTIONS,
|
||||
suspicious,
|
||||
"suspicious combination of options for opening a file"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
|
||||
|
@ -4033,6 +4071,7 @@ impl_lint_pass!(Methods => [
|
|||
MAP_ERR_IGNORE,
|
||||
MUT_MUTEX_LOCK,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
SUSPICIOUS_OPEN_OPTIONS,
|
||||
PATH_BUF_PUSH_OVERWRITE,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
REPEAT_ONCE,
|
||||
|
|
|
@ -1,46 +1,74 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, match_type};
|
||||
use clippy_utils::{match_any_def_paths, paths};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::NONSENSICAL_OPEN_OPTIONS;
|
||||
use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS};
|
||||
|
||||
fn is_open_options(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||
is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || match_type(cx, ty, &paths::TOKIO_IO_OPEN_OPTIONS)
|
||||
}
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
|
||||
&& is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::FsOpenOptions)
|
||||
&& is_open_options(cx, cx.tcx.type_of(impl_id).instantiate_identity())
|
||||
{
|
||||
let mut options = Vec::new();
|
||||
get_open_options(cx, recv, &mut options);
|
||||
check_open_options(cx, &options, e.span);
|
||||
if get_open_options(cx, recv, &mut options) {
|
||||
check_open_options(cx, &options, e.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
enum Argument {
|
||||
True,
|
||||
False,
|
||||
Set(bool),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
||||
enum OpenOption {
|
||||
Write,
|
||||
Append,
|
||||
Create,
|
||||
CreateNew,
|
||||
Read,
|
||||
Truncate,
|
||||
Create,
|
||||
Append,
|
||||
Write,
|
||||
}
|
||||
impl std::fmt::Display for OpenOption {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
OpenOption::Append => write!(f, "append"),
|
||||
OpenOption::Create => write!(f, "create"),
|
||||
OpenOption::CreateNew => write!(f, "create_new"),
|
||||
OpenOption::Read => write!(f, "read"),
|
||||
OpenOption::Truncate => write!(f, "truncate"),
|
||||
OpenOption::Write => write!(f, "write"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
|
||||
if let ExprKind::MethodCall(path, receiver, arguments, _) = argument.kind {
|
||||
/// Collects information about a method call chain on `OpenOptions`.
|
||||
/// Returns false if an unexpected expression kind was found "on the way",
|
||||
/// and linting should then be avoided.
|
||||
fn get_open_options(
|
||||
cx: &LateContext<'_>,
|
||||
argument: &Expr<'_>,
|
||||
options: &mut Vec<(OpenOption, Argument, Span)>,
|
||||
) -> bool {
|
||||
if let ExprKind::MethodCall(path, receiver, arguments, span) = argument.kind {
|
||||
let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
|
||||
|
||||
// Only proceed if this is a call on some object of type std::fs::OpenOptions
|
||||
if is_type_diagnostic_item(cx, obj_ty, sym::FsOpenOptions) && !arguments.is_empty() {
|
||||
if !arguments.is_empty() && is_open_options(cx, obj_ty) {
|
||||
let argument_option = match arguments[0].kind {
|
||||
ExprKind::Lit(span) => {
|
||||
if let Spanned {
|
||||
|
@ -48,11 +76,12 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
|
|||
..
|
||||
} = span
|
||||
{
|
||||
if *lit { Argument::True } else { Argument::False }
|
||||
Argument::Set(*lit)
|
||||
} else {
|
||||
// The function is called with a literal which is not a boolean literal.
|
||||
// This is theoretically possible, but not very likely.
|
||||
return;
|
||||
// We'll ignore it for now
|
||||
return get_open_options(cx, receiver, options);
|
||||
}
|
||||
},
|
||||
_ => Argument::Unknown,
|
||||
|
@ -60,106 +89,77 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
|
|||
|
||||
match path.ident.as_str() {
|
||||
"create" => {
|
||||
options.push((OpenOption::Create, argument_option));
|
||||
options.push((OpenOption::Create, argument_option, span));
|
||||
},
|
||||
"create_new" => {
|
||||
options.push((OpenOption::CreateNew, argument_option, span));
|
||||
},
|
||||
"append" => {
|
||||
options.push((OpenOption::Append, argument_option));
|
||||
options.push((OpenOption::Append, argument_option, span));
|
||||
},
|
||||
"truncate" => {
|
||||
options.push((OpenOption::Truncate, argument_option));
|
||||
options.push((OpenOption::Truncate, argument_option, span));
|
||||
},
|
||||
"read" => {
|
||||
options.push((OpenOption::Read, argument_option));
|
||||
options.push((OpenOption::Read, argument_option, span));
|
||||
},
|
||||
"write" => {
|
||||
options.push((OpenOption::Write, argument_option));
|
||||
options.push((OpenOption::Write, argument_option, span));
|
||||
},
|
||||
_ => {
|
||||
// Avoid linting altogether if this method is from a trait.
|
||||
// This might be a user defined extension trait with a method like `truncate_write`
|
||||
// which would be a false positive
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(argument.hir_id)
|
||||
&& cx.tcx.trait_of_item(method_def_id).is_some()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
get_open_options(cx, receiver, options);
|
||||
get_open_options(cx, receiver, options)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else if let ExprKind::Call(callee, _) = argument.kind
|
||||
&& let ExprKind::Path(path) = callee.kind
|
||||
&& let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id()
|
||||
{
|
||||
match_any_def_paths(
|
||||
cx,
|
||||
did,
|
||||
&[
|
||||
&paths::TOKIO_IO_OPEN_OPTIONS_NEW,
|
||||
&paths::OPEN_OPTIONS_NEW,
|
||||
&paths::FILE_OPTIONS,
|
||||
&paths::TOKIO_FILE_OPTIONS,
|
||||
],
|
||||
)
|
||||
.is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], span: Span) {
|
||||
let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
|
||||
let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
|
||||
(false, false, false, false, false);
|
||||
// This code is almost duplicated (oh, the irony), but I haven't found a way to
|
||||
// unify it.
|
||||
|
||||
for option in options {
|
||||
match *option {
|
||||
(OpenOption::Create, arg) => {
|
||||
if create {
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
span,
|
||||
"the method `create` is called more than once",
|
||||
);
|
||||
} else {
|
||||
create = true;
|
||||
}
|
||||
create_arg = create_arg || (arg == Argument::True);
|
||||
},
|
||||
(OpenOption::Append, arg) => {
|
||||
if append {
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
span,
|
||||
"the method `append` is called more than once",
|
||||
);
|
||||
} else {
|
||||
append = true;
|
||||
}
|
||||
append_arg = append_arg || (arg == Argument::True);
|
||||
},
|
||||
(OpenOption::Truncate, arg) => {
|
||||
if truncate {
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
span,
|
||||
"the method `truncate` is called more than once",
|
||||
);
|
||||
} else {
|
||||
truncate = true;
|
||||
}
|
||||
truncate_arg = truncate_arg || (arg == Argument::True);
|
||||
},
|
||||
(OpenOption::Read, arg) => {
|
||||
if read {
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
span,
|
||||
"the method `read` is called more than once",
|
||||
);
|
||||
} else {
|
||||
read = true;
|
||||
}
|
||||
read_arg = read_arg || (arg == Argument::True);
|
||||
},
|
||||
(OpenOption::Write, arg) => {
|
||||
if write {
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
span,
|
||||
"the method `write` is called more than once",
|
||||
);
|
||||
} else {
|
||||
write = true;
|
||||
}
|
||||
write_arg = write_arg || (arg == Argument::True);
|
||||
},
|
||||
fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument, Span)], span: Span) {
|
||||
// The args passed to these methods, if they have been called
|
||||
let mut options = FxHashMap::default();
|
||||
for (option, arg, sp) in settings {
|
||||
if let Some((_, prev_span)) = options.insert(option.clone(), (arg.clone(), *sp)) {
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
prev_span,
|
||||
&format!("the method `{}` is called more than once", &option),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
|
||||
if let Some((Argument::Set(true), _)) = options.get(&OpenOption::Read)
|
||||
&& let Some((Argument::Set(true), _)) = options.get(&OpenOption::Truncate)
|
||||
&& let None | Some((Argument::Set(false), _)) = options.get(&OpenOption::Write)
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
|
@ -167,7 +167,10 @@ fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)],
|
|||
"file opened with `truncate` and `read`",
|
||||
);
|
||||
}
|
||||
if append && truncate && append_arg && truncate_arg {
|
||||
|
||||
if let Some((Argument::Set(true), _)) = options.get(&OpenOption::Append)
|
||||
&& let Some((Argument::Set(true), _)) = options.get(&OpenOption::Truncate)
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
|
@ -175,4 +178,29 @@ fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)],
|
|||
"file opened with `append` and `truncate`",
|
||||
);
|
||||
}
|
||||
|
||||
if let Some((Argument::Set(true), create_span)) = options.get(&OpenOption::Create)
|
||||
&& let None = options.get(&OpenOption::Truncate)
|
||||
&& let None | Some((Argument::Set(false), _)) = options.get(&OpenOption::Append)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SUSPICIOUS_OPEN_OPTIONS,
|
||||
*create_span,
|
||||
"file opened with `create`, but `truncate` behavior not defined",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
create_span.shrink_to_hi(),
|
||||
"add",
|
||||
".truncate(true)".to_string(),
|
||||
rustc_errors::Applicability::MaybeIncorrect,
|
||||
)
|
||||
.help("if you intend to overwrite an existing file entirely, call `.truncate(true)`")
|
||||
.help(
|
||||
"if you instead know that you may want to keep some parts of the old file, call `.truncate(false)`",
|
||||
)
|
||||
.help("alternatively, use `.append(true)` to append to the file instead of overwriting it");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ pub(super) fn check(
|
|||
};
|
||||
let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" };
|
||||
let hint = format!("{}.{method_hint}()", snippet(cx, as_ref_recv.span, ".."));
|
||||
let suggestion = format!("try using {method_hint} instead");
|
||||
let suggestion = format!("consider using {method_hint}");
|
||||
|
||||
let msg = format!("called `{current_method}` on an `Option` value");
|
||||
span_lint_and_sugg(
|
||||
|
|
|
@ -33,7 +33,7 @@ pub(super) fn check<'tcx>(
|
|||
OPTION_MAP_OR_ERR_OK,
|
||||
expr.span,
|
||||
msg,
|
||||
"try using `ok_or` instead",
|
||||
"consider using `ok_or`",
|
||||
format!("{self_snippet}.ok_or({err_snippet})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
|
|
@ -72,7 +72,7 @@ pub(super) fn check<'tcx>(
|
|||
OPTION_MAP_OR_NONE,
|
||||
expr.span,
|
||||
msg,
|
||||
"try using `map` instead",
|
||||
"consider using `map`",
|
||||
format!("{self_snippet}.map({arg_snippet} {func_snippet})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
@ -85,7 +85,7 @@ pub(super) fn check<'tcx>(
|
|||
OPTION_MAP_OR_NONE,
|
||||
expr.span,
|
||||
msg,
|
||||
"try using `and_then` instead",
|
||||
"consider using `and_then`",
|
||||
format!("{self_snippet}.and_then({func_snippet})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
@ -97,7 +97,7 @@ pub(super) fn check<'tcx>(
|
|||
RESULT_MAP_OR_INTO_OPTION,
|
||||
expr.span,
|
||||
msg,
|
||||
"try using `ok` instead",
|
||||
"consider using `ok`",
|
||||
format!("{self_snippet}.ok()"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
|
|
@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(
|
|||
RESULT_MAP_OR_INTO_OPTION,
|
||||
expr.span,
|
||||
msg,
|
||||
"try using `ok` instead",
|
||||
"consider using `ok`",
|
||||
format!("{self_snippet}.ok()"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
|
|
@ -63,7 +63,7 @@ pub(super) fn check<'tcx>(
|
|||
SEARCH_IS_SOME,
|
||||
method_span.with_hi(expr.span.hi()),
|
||||
&msg,
|
||||
"use `any()` instead",
|
||||
"consider using",
|
||||
format!(
|
||||
"any({})",
|
||||
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
|
||||
|
@ -77,7 +77,7 @@ pub(super) fn check<'tcx>(
|
|||
SEARCH_IS_SOME,
|
||||
expr.span,
|
||||
&msg,
|
||||
"use `!_.any()` instead",
|
||||
"consider using",
|
||||
format!(
|
||||
"!{iter}.any({})",
|
||||
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
|
||||
|
@ -118,7 +118,7 @@ pub(super) fn check<'tcx>(
|
|||
SEARCH_IS_SOME,
|
||||
method_span.with_hi(expr.span.hi()),
|
||||
&msg,
|
||||
"use `contains()` instead",
|
||||
"consider using",
|
||||
format!("contains({find_arg})"),
|
||||
applicability,
|
||||
);
|
||||
|
@ -132,7 +132,7 @@ pub(super) fn check<'tcx>(
|
|||
SEARCH_IS_SOME,
|
||||
expr.span,
|
||||
&msg,
|
||||
"use `!_.contains()` instead",
|
||||
"consider using",
|
||||
format!("!{string}.contains({find_arg})"),
|
||||
applicability,
|
||||
);
|
||||
|
|
|
@ -57,7 +57,7 @@ pub(super) fn check(
|
|||
SINGLE_CHAR_PATTERN,
|
||||
arg.span,
|
||||
"single-character string constant used as pattern",
|
||||
"try using a `char` instead",
|
||||
"consider using a `char`",
|
||||
hint,
|
||||
applicability,
|
||||
);
|
||||
|
|
|
@ -32,7 +32,7 @@ pub(super) fn check<'tcx>(
|
|||
UNNECESSARY_JOIN,
|
||||
span.with_hi(expr.span.hi()),
|
||||
r#"called `.collect::<Vec<String>>().join("")` on an iterator"#,
|
||||
"try using",
|
||||
"consider using",
|
||||
"collect::<String>()".to_owned(),
|
||||
applicability,
|
||||
);
|
||||
|
|
|
@ -203,7 +203,7 @@ pub(super) fn check<'tcx>(
|
|||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort_by_key here instead",
|
||||
"consider using `sort_by_key`",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}_by_key(|{}| {})",
|
||||
|
@ -226,7 +226,7 @@ pub(super) fn check<'tcx>(
|
|||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort here instead",
|
||||
"consider using `sort`",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}()",
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
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, is_diag_trait_item, match_def_path, paths, peel_blocks};
|
||||
use clippy_utils::{
|
||||
get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, paths, peel_blocks, strip_pat_refs,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -108,9 +110,12 @@ fn check_qpath(cx: &LateContext<'_>, qpath: hir::QPath<'_>, hir_id: hir::HirId)
|
|||
|
||||
fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
|
||||
match arg.kind {
|
||||
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
|
||||
hir::ExprKind::Closure(&hir::Closure { body, .. })
|
||||
// If it's a closure, we need to check what is called.
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
if let closure_body = cx.tcx.hir().body(body)
|
||||
&& let [param] = closure_body.params
|
||||
&& let hir::PatKind::Binding(_, local_id, ..) = strip_pat_refs(param.pat).kind =>
|
||||
{
|
||||
let closure_expr = peel_blocks(closure_body.value);
|
||||
match closure_expr.kind {
|
||||
hir::ExprKind::MethodCall(method, obj, [], _) => {
|
||||
|
@ -122,14 +127,17 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
|
|||
// no autoderefs
|
||||
&& !cx.typeck_results().expr_adjustments(obj).iter()
|
||||
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
|
||||
&& path_to_local_id(obj, local_id)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Call(call, [_]) => {
|
||||
if let hir::ExprKind::Path(qpath) = call.kind {
|
||||
hir::ExprKind::Call(call, [recv]) => {
|
||||
if let hir::ExprKind::Path(qpath) = call.kind
|
||||
&& path_to_local_id(recv, local_id)
|
||||
{
|
||||
check_qpath(cx, qpath, call.hir_id)
|
||||
} else {
|
||||
false
|
||||
|
|
|
@ -5,16 +5,15 @@ use clippy_utils::visitors::for_each_expr_with_closures;
|
|||
use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
BlockCheckMode, Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node,
|
||||
PatKind, QPath,
|
||||
PatKind,
|
||||
};
|
||||
use rustc_hir_typeck::expr_use_visitor as euv;
|
||||
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::associated_body;
|
||||
use rustc_middle::hir::nested_filter::OnlyBodies;
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
@ -234,12 +233,29 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
|
||||
cx.tcx.hir().visit_all_item_likes_in_crate(&mut FnNeedsMutVisitor {
|
||||
cx,
|
||||
used_fn_def_ids: &mut self.used_fn_def_ids,
|
||||
});
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
// #11182; do not lint if mutability is required elsewhere
|
||||
if let ExprKind::Path(..) = expr.kind
|
||||
&& let Some(parent) = get_parent_node(cx.tcx, expr.hir_id)
|
||||
&& let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(expr).kind()
|
||||
&& let Some(def_id) = def_id.as_local()
|
||||
{
|
||||
if let Node::Expr(e) = parent
|
||||
&& let ExprKind::Call(call, _) = e.kind
|
||||
&& call.hir_id == expr.hir_id
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't need to check each argument individually as you cannot coerce a function
|
||||
// taking `&mut` -> `&`, for some reason, so if we've gotten this far we know it's
|
||||
// passed as a `fn`-like argument (or is unified) and should ignore every "unused"
|
||||
// argument entirely
|
||||
self.used_fn_def_ids.insert(def_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
|
||||
for (fn_def_id, unused) in self
|
||||
.fn_def_ids_to_maybe_unused_mut
|
||||
.iter()
|
||||
|
@ -501,48 +517,3 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A final pass to check for paths referencing this function that require the argument to be
|
||||
/// `&mut`, basically if the function is ever used as a `fn`-like argument.
|
||||
struct FnNeedsMutVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
used_fn_def_ids: &'a mut FxHashSet<LocalDefId>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> {
|
||||
type NestedFilter = OnlyBodies;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.cx.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_qpath(&mut self, qpath: &'tcx QPath<'tcx>, hir_id: HirId, _: Span) {
|
||||
walk_qpath(self, qpath, hir_id);
|
||||
|
||||
let Self { cx, used_fn_def_ids } = self;
|
||||
|
||||
// #11182; do not lint if mutability is required elsewhere
|
||||
if let Node::Expr(expr) = cx.tcx.hir_node(hir_id)
|
||||
&& let Some(parent) = get_parent_node(cx.tcx, expr.hir_id)
|
||||
&& let ty::FnDef(def_id, _) = cx
|
||||
.tcx
|
||||
.typeck(cx.tcx.hir().enclosing_body_owner(hir_id))
|
||||
.expr_ty(expr)
|
||||
.kind()
|
||||
&& let Some(def_id) = def_id.as_local()
|
||||
{
|
||||
if let Node::Expr(e) = parent
|
||||
&& let ExprKind::Call(call, _) = e.kind
|
||||
&& call.hir_id == expr.hir_id
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't need to check each argument individually as you cannot coerce a function
|
||||
// taking `&mut` -> `&`, for some reason, so if we've gotten this far we know it's
|
||||
// passed as a `fn`-like argument (or is unified) and should ignore every "unused"
|
||||
// argument entirely
|
||||
used_fn_def_ids.insert(def_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::has_drop;
|
||||
use clippy_utils::{get_parent_node, is_lint_allowed, peel_blocks};
|
||||
use clippy_utils::{any_parent_is_automatically_derived, get_parent_node, is_lint_allowed, path_to_local, peel_blocks};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{
|
||||
is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, ItemKind, Node, PatKind, Stmt, StmtKind, UnsafeSource,
|
||||
is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, Node, PatKind, Stmt,
|
||||
StmtKind, UnsafeSource,
|
||||
};
|
||||
use rustc_infer::infer::TyCtxtInferExt as _;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use std::ops::Deref;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -74,94 +76,125 @@ declare_clippy_lint! {
|
|||
"outer expressions with no effect"
|
||||
}
|
||||
|
||||
declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION, NO_EFFECT_UNDERSCORE_BINDING]);
|
||||
#[derive(Default)]
|
||||
pub struct NoEffect {
|
||||
underscore_bindings: HirIdMap<Span>,
|
||||
local_bindings: Vec<Vec<HirId>>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION, NO_EFFECT_UNDERSCORE_BINDING]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for NoEffect {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
if check_no_effect(cx, stmt) {
|
||||
if self.check_no_effect(cx, stmt) {
|
||||
return;
|
||||
}
|
||||
check_unnecessary_operation(cx, stmt);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||
if let StmtKind::Semi(expr) = stmt.kind {
|
||||
// move `expr.span.from_expansion()` ahead
|
||||
if expr.span.from_expansion() {
|
||||
return false;
|
||||
}
|
||||
let expr = peel_blocks(expr);
|
||||
fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx>) {
|
||||
self.local_bindings.push(Vec::default());
|
||||
}
|
||||
|
||||
if is_operator_overridden(cx, expr) {
|
||||
// Return `true`, to prevent `check_unnecessary_operation` from
|
||||
// linting on this statement as well.
|
||||
return true;
|
||||
}
|
||||
if has_no_effect(cx, expr) {
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
NO_EFFECT,
|
||||
expr.hir_id,
|
||||
stmt.span,
|
||||
"statement with no effect",
|
||||
|diag| {
|
||||
for parent in cx.tcx.hir().parent_iter(stmt.hir_id) {
|
||||
if let Node::Item(item) = parent.1
|
||||
&& let ItemKind::Fn(..) = item.kind
|
||||
&& let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id)
|
||||
&& let [.., final_stmt] = block.stmts
|
||||
&& final_stmt.hir_id == stmt.hir_id
|
||||
{
|
||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||
let mut ret_ty = cx
|
||||
.tcx
|
||||
.fn_sig(item.owner_id)
|
||||
.instantiate_identity()
|
||||
.output()
|
||||
.skip_binder();
|
||||
|
||||
// Remove `impl Future<Output = T>` to get `T`
|
||||
if cx.tcx.ty_is_opaque_future(ret_ty)
|
||||
&& let Some(true_ret_ty) = cx.tcx.infer_ctxt().build().get_impl_future_output_ty(ret_ty)
|
||||
{
|
||||
ret_ty = true_ret_ty;
|
||||
}
|
||||
|
||||
if !ret_ty.is_unit() && ret_ty == expr_ty {
|
||||
diag.span_suggestion(
|
||||
stmt.span.shrink_to_lo(),
|
||||
"did you mean to return it?",
|
||||
"return ",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
} else if let StmtKind::Local(local) = stmt.kind {
|
||||
if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id)
|
||||
&& let Some(init) = local.init
|
||||
&& local.els.is_none()
|
||||
&& !local.pat.span.from_expansion()
|
||||
&& has_no_effect(cx, init)
|
||||
&& let PatKind::Binding(_, _, ident, _) = local.pat.kind
|
||||
&& ident.name.to_ident_string().starts_with('_')
|
||||
{
|
||||
span_lint_hir(
|
||||
cx,
|
||||
NO_EFFECT_UNDERSCORE_BINDING,
|
||||
init.hir_id,
|
||||
stmt.span,
|
||||
"binding to `_` prefixed variable with no side-effect",
|
||||
);
|
||||
return true;
|
||||
fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx>) {
|
||||
for hir_id in self.local_bindings.pop().unwrap() {
|
||||
if let Some(span) = self.underscore_bindings.remove(&hir_id) {
|
||||
span_lint_hir(
|
||||
cx,
|
||||
NO_EFFECT_UNDERSCORE_BINDING,
|
||||
hir_id,
|
||||
span,
|
||||
"binding to `_` prefixed variable with no side-effect",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
|
||||
fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
|
||||
if let Some(def_id) = path_to_local(expr) {
|
||||
self.underscore_bindings.remove(&def_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NoEffect {
|
||||
fn check_no_effect(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||
if let StmtKind::Semi(expr) = stmt.kind {
|
||||
// move `expr.span.from_expansion()` ahead
|
||||
if expr.span.from_expansion() {
|
||||
return false;
|
||||
}
|
||||
let expr = peel_blocks(expr);
|
||||
|
||||
if is_operator_overridden(cx, expr) {
|
||||
// Return `true`, to prevent `check_unnecessary_operation` from
|
||||
// linting on this statement as well.
|
||||
return true;
|
||||
}
|
||||
if has_no_effect(cx, expr) {
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
NO_EFFECT,
|
||||
expr.hir_id,
|
||||
stmt.span,
|
||||
"statement with no effect",
|
||||
|diag| {
|
||||
for parent in cx.tcx.hir().parent_iter(stmt.hir_id) {
|
||||
if let Node::Item(item) = parent.1
|
||||
&& let ItemKind::Fn(..) = item.kind
|
||||
&& let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id)
|
||||
&& let [.., final_stmt] = block.stmts
|
||||
&& final_stmt.hir_id == stmt.hir_id
|
||||
{
|
||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||
let mut ret_ty = cx
|
||||
.tcx
|
||||
.fn_sig(item.owner_id)
|
||||
.instantiate_identity()
|
||||
.output()
|
||||
.skip_binder();
|
||||
|
||||
// Remove `impl Future<Output = T>` to get `T`
|
||||
if cx.tcx.ty_is_opaque_future(ret_ty)
|
||||
&& let Some(true_ret_ty) =
|
||||
cx.tcx.infer_ctxt().build().get_impl_future_output_ty(ret_ty)
|
||||
{
|
||||
ret_ty = true_ret_ty;
|
||||
}
|
||||
|
||||
if !ret_ty.is_unit() && ret_ty == expr_ty {
|
||||
diag.span_suggestion(
|
||||
stmt.span.shrink_to_lo(),
|
||||
"did you mean to return it?",
|
||||
"return ",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
} else if let StmtKind::Local(local) = stmt.kind {
|
||||
if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id)
|
||||
&& let Some(init) = local.init
|
||||
&& local.els.is_none()
|
||||
&& !local.pat.span.from_expansion()
|
||||
&& has_no_effect(cx, init)
|
||||
&& let PatKind::Binding(_, hir_id, ident, _) = local.pat.kind
|
||||
&& ident.name.to_ident_string().starts_with('_')
|
||||
&& !any_parent_is_automatically_derived(cx.tcx, local.hir_id)
|
||||
{
|
||||
if let Some(l) = self.local_bindings.last_mut() {
|
||||
l.push(hir_id);
|
||||
self.underscore_bindings.insert(hir_id, ident.span);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_operator_overridden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::std_or_core;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::PTR_EQ;
|
||||
|
||||
static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
|
@ -26,13 +25,14 @@ pub(super) fn check<'tcx>(
|
|||
&& let Some(left_snip) = snippet_opt(cx, left_var.span)
|
||||
&& let Some(right_snip) = snippet_opt(cx, right_var.span)
|
||||
{
|
||||
let Some(top_crate) = std_or_core(cx) else { return };
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PTR_EQ,
|
||||
expr.span,
|
||||
LINT_MSG,
|
||||
&format!("use `{top_crate}::ptr::eq` when comparing raw pointers"),
|
||||
"try",
|
||||
format!("std::ptr::eq({left_snip}, {right_snip})"),
|
||||
format!("{top_crate}::ptr::eq({left_snip}, {right_snip})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields {
|
|||
};
|
||||
|
||||
let is_visible = |field: &FieldDef<'_>| match self.behavior {
|
||||
PubUnderscoreFieldsBehaviour::PublicallyExported => cx.effective_visibilities.is_reachable(field.def_id),
|
||||
PubUnderscoreFieldsBehaviour::PubliclyExported => cx.effective_visibilities.is_reachable(field.def_id),
|
||||
PubUnderscoreFieldsBehaviour::AllPubFields => {
|
||||
// If there is a visibility span then the field is marked pub in some way.
|
||||
!field.vis_span.is_empty()
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::get_enclosing_block;
|
||||
use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use core::ops::ControlFlow;
|
||||
use hir::{Expr, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind};
|
||||
|
||||
use hir::{Expr, ExprKind, HirId, Local, PatKind, PathSegment, QPath, StmtKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
|
@ -49,57 +51,40 @@ declare_lint_pass!(ReadZeroByteVec => [READ_ZERO_BYTE_VEC]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &hir::Block<'tcx>) {
|
||||
for (idx, stmt) in block.stmts.iter().enumerate() {
|
||||
if !stmt.span.from_expansion()
|
||||
// matches `let v = Vec::new();`
|
||||
&& let StmtKind::Local(local) = stmt.kind
|
||||
&& let Local { pat, init: Some(init), .. } = local
|
||||
&& let PatKind::Binding(_, _, ident, _) = pat.kind
|
||||
for stmt in block.stmts {
|
||||
if stmt.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let StmtKind::Local(local) = stmt.kind
|
||||
&& let Local {
|
||||
pat, init: Some(init), ..
|
||||
} = local
|
||||
&& let PatKind::Binding(_, id, ident, _) = pat.kind
|
||||
&& let Some(vec_init_kind) = get_vec_init_kind(cx, init)
|
||||
{
|
||||
let visitor = |expr: &Expr<'_>| {
|
||||
if let ExprKind::MethodCall(path, _, [arg], _) = expr.kind
|
||||
&& let PathSegment {
|
||||
ident: read_or_read_exact,
|
||||
..
|
||||
} = *path
|
||||
&& matches!(read_or_read_exact.as_str(), "read" | "read_exact")
|
||||
&& let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(None, inner_path)) = inner.kind
|
||||
&& let [inner_seg] = inner_path.segments
|
||||
&& ident.name == inner_seg.ident.name
|
||||
{
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
let mut visitor = ReadVecVisitor {
|
||||
local_id: id,
|
||||
read_zero_expr: None,
|
||||
has_resize: false,
|
||||
};
|
||||
|
||||
let (read_found, next_stmt_span) = if let Some(next_stmt) = block.stmts.get(idx + 1) {
|
||||
// case { .. stmt; stmt; .. }
|
||||
(for_each_expr(next_stmt, visitor).is_some(), next_stmt.span)
|
||||
} else if let Some(e) = block.expr {
|
||||
// case { .. stmt; expr }
|
||||
(for_each_expr(e, visitor).is_some(), e.span)
|
||||
} else {
|
||||
let Some(enclosing_block) = get_enclosing_block(cx, id) else {
|
||||
return;
|
||||
};
|
||||
visitor.visit_block(enclosing_block);
|
||||
|
||||
if read_found && !next_stmt_span.from_expansion() {
|
||||
if let Some(expr) = visitor.read_zero_expr {
|
||||
let applicability = Applicability::MaybeIncorrect;
|
||||
match vec_init_kind {
|
||||
VecInitKind::WithConstCapacity(len) => {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
READ_ZERO_BYTE_VEC,
|
||||
next_stmt_span,
|
||||
expr.span,
|
||||
"reading zero byte data to `Vec`",
|
||||
"try",
|
||||
format!(
|
||||
"{}.resize({len}, 0); {}",
|
||||
ident.as_str(),
|
||||
snippet(cx, next_stmt_span, "..")
|
||||
),
|
||||
format!("{}.resize({len}, 0); {}", ident.as_str(), snippet(cx, expr.span, "..")),
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
|
@ -108,25 +93,20 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
|
|||
span_lint_and_sugg(
|
||||
cx,
|
||||
READ_ZERO_BYTE_VEC,
|
||||
next_stmt_span,
|
||||
expr.span,
|
||||
"reading zero byte data to `Vec`",
|
||||
"try",
|
||||
format!(
|
||||
"{}.resize({}, 0); {}",
|
||||
ident.as_str(),
|
||||
snippet(cx, e.span, ".."),
|
||||
snippet(cx, next_stmt_span, "..")
|
||||
snippet(cx, expr.span, "..")
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
_ => {
|
||||
span_lint(
|
||||
cx,
|
||||
READ_ZERO_BYTE_VEC,
|
||||
next_stmt_span,
|
||||
"reading zero byte data to `Vec`",
|
||||
);
|
||||
span_lint(cx, READ_ZERO_BYTE_VEC, expr.span, "reading zero byte data to `Vec`");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -134,3 +114,47 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ReadVecVisitor<'tcx> {
|
||||
local_id: HirId,
|
||||
read_zero_expr: Option<&'tcx Expr<'tcx>>,
|
||||
has_resize: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for ReadVecVisitor<'tcx> {
|
||||
fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
|
||||
if let ExprKind::MethodCall(path, receiver, args, _) = e.kind {
|
||||
let PathSegment { ident, .. } = *path;
|
||||
|
||||
match ident.as_str() {
|
||||
"read" | "read_exact" => {
|
||||
let [arg] = args else { return };
|
||||
if let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(None, inner_path)) = inner.kind
|
||||
&& let [inner_seg] = inner_path.segments
|
||||
&& let Res::Local(res_id) = inner_seg.res
|
||||
&& self.local_id == res_id
|
||||
{
|
||||
self.read_zero_expr = Some(e);
|
||||
return;
|
||||
}
|
||||
},
|
||||
"resize" => {
|
||||
// If the Vec is resized, then it's a valid read
|
||||
if let ExprKind::Path(QPath::Resolved(_, inner_path)) = receiver.kind
|
||||
&& let Res::Local(res_id) = inner_path.res
|
||||
&& self.local_id == res_id
|
||||
{
|
||||
self.has_resize = true;
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
if !self.has_resize && self.read_zero_expr.is_none() {
|
||||
walk_expr(self, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::{Block, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{ExpnKind, MacroKind, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -39,6 +40,7 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned {
|
|||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
|
||||
if !block.span.from_expansion()
|
||||
&& let Some(expr) = block.expr
|
||||
&& !from_attr_macro(expr.span)
|
||||
&& let t_expr = cx.typeck_results().expr_ty(expr)
|
||||
&& t_expr.is_unit()
|
||||
&& let mut app = Applicability::MachineApplicable
|
||||
|
@ -63,3 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_attr_macro(span: Span) -> bool {
|
||||
matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Attr, _))
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::{is_from_proc_macro, is_in_test_function};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
|
@ -88,16 +88,18 @@ impl<'tcx> LateLintPass<'tcx> for SingleCallFn {
|
|||
};
|
||||
cx.tcx.hir().visit_all_item_likes_in_crate(&mut v);
|
||||
|
||||
for usage in self.def_id_to_usage.values() {
|
||||
for (&def_id, usage) in &self.def_id_to_usage {
|
||||
let single_call_fn_span = usage.0;
|
||||
if let [caller_span] = *usage.1 {
|
||||
span_lint_and_help(
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
SINGLE_CALL_FN,
|
||||
cx.tcx.local_def_id_to_hir_id(def_id),
|
||||
single_call_fn_span,
|
||||
"this function is only used once",
|
||||
Some(caller_span),
|
||||
"used here",
|
||||
|diag| {
|
||||
diag.span_help(caller_span, "used here");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -390,6 +390,14 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'
|
|||
}
|
||||
}
|
||||
|
||||
fn get_ty_res(ty: Ty<'_>) -> Option<Res> {
|
||||
match ty.kind {
|
||||
TyKind::Path(QPath::Resolved(_, path)) => Some(path.res),
|
||||
TyKind::Path(QPath::TypeRelative(ty, _)) => get_ty_res(*ty),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds
|
||||
fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
|
||||
ComparableTraitRef(
|
||||
|
@ -401,10 +409,8 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
|
|||
.filter_map(|segment| {
|
||||
// get trait bound type arguments
|
||||
Some(segment.args?.args.iter().filter_map(|arg| {
|
||||
if let GenericArg::Type(ty) = arg
|
||||
&& let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
|
||||
{
|
||||
return Some(path.res);
|
||||
if let GenericArg::Type(ty) = arg {
|
||||
return get_ty_res(**ty);
|
||||
}
|
||||
None
|
||||
}))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::TRANSMUTE_INT_TO_CHAR;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::sugg;
|
||||
use clippy_utils::{std_or_core, sugg};
|
||||
use rustc_ast as ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
|
@ -25,6 +25,7 @@ pub(super) fn check<'tcx>(
|
|||
e.span,
|
||||
&format!("transmute from a `{from_ty}` to a `char`"),
|
||||
|diag| {
|
||||
let Some(top_crate) = std_or_core(cx) else { return };
|
||||
let arg = sugg::Sugg::hir(cx, arg, "..");
|
||||
let arg = if let ty::Int(_) = from_ty.kind() {
|
||||
arg.as_ty(ast::UintTy::U32.name_str())
|
||||
|
@ -34,7 +35,7 @@ pub(super) fn check<'tcx>(
|
|||
diag.span_suggestion(
|
||||
e.span,
|
||||
"consider using",
|
||||
format!("std::char::from_u32({arg}).unwrap()"),
|
||||
format!("{top_crate}::char::from_u32({arg}).unwrap()"),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::{TRANSMUTE_BYTES_TO_STR, TRANSMUTE_PTR_TO_PTR};
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::sugg;
|
||||
use clippy_utils::{std_or_core, sugg};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Mutability};
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -25,6 +25,8 @@ pub(super) fn check<'tcx>(
|
|||
&& let ty::Uint(ty::UintTy::U8) = slice_ty.kind()
|
||||
&& from_mutbl == to_mutbl
|
||||
{
|
||||
let Some(top_crate) = std_or_core(cx) else { return true };
|
||||
|
||||
let postfix = if *from_mutbl == Mutability::Mut { "_mut" } else { "" };
|
||||
|
||||
let snippet = snippet(cx, arg.span, "..");
|
||||
|
@ -36,9 +38,9 @@ pub(super) fn check<'tcx>(
|
|||
&format!("transmute from a `{from_ty}` to a `{to_ty}`"),
|
||||
"consider using",
|
||||
if const_context {
|
||||
format!("std::str::from_utf8_unchecked{postfix}({snippet})")
|
||||
format!("{top_crate}::str::from_utf8_unchecked{postfix}({snippet})")
|
||||
} else {
|
||||
format!("std::str::from_utf8{postfix}({snippet}).unwrap()")
|
||||
format!("{top_crate}::str::from_utf8{postfix}({snippet}).unwrap()")
|
||||
},
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
|
|
@ -167,7 +167,15 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca
|
|||
false
|
||||
}
|
||||
},
|
||||
ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => {
|
||||
ExprKind::MethodCall(segment, receiver, &[_arg], _) if segment.ident.name == name.name => {
|
||||
if let Some(ty) = cx.typeck_results().expr_ty_opt(receiver)
|
||||
&& let Some(ty_id) = get_ty_def_id(ty)
|
||||
&& self_arg != ty_id
|
||||
{
|
||||
// Since this called on a different type, the lint should not be
|
||||
// triggered here.
|
||||
return;
|
||||
}
|
||||
if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
|
||||
&& trait_id == trait_def_id
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::{is_trait_method, is_try, match_trait_method, paths};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths};
|
||||
use hir::{ExprKind, PatKind};
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -45,126 +46,219 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
|
||||
let (hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr)) = s.kind else {
|
||||
return;
|
||||
};
|
||||
#[derive(Copy, Clone)]
|
||||
enum IoOp {
|
||||
AsyncWrite(bool),
|
||||
AsyncRead(bool),
|
||||
SyncRead(bool),
|
||||
SyncWrite(bool),
|
||||
}
|
||||
|
||||
match expr.kind {
|
||||
hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => {
|
||||
if let hir::ExprKind::Call(func, [ref arg_0, ..]) = res.kind {
|
||||
if matches!(
|
||||
func.kind,
|
||||
hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..))
|
||||
) {
|
||||
check_map_error(cx, arg_0, expr);
|
||||
}
|
||||
} else {
|
||||
check_map_error(cx, res, expr);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::MethodCall(path, arg_0, ..) => match path.ident.as_str() {
|
||||
"expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" | "is_ok" | "is_err" => {
|
||||
check_map_error(cx, arg_0, expr);
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
|
||||
/// We perform the check on the block level.
|
||||
/// If we want to catch match and if expressions that act as returns of the block
|
||||
/// we need to check them at `check_expr` or `check_block` as they are not stmts
|
||||
/// but we can't check them at `check_expr` because we need the broader context
|
||||
/// because we should do this only for the final expression of the block, and not for
|
||||
/// `StmtKind::Local` which binds values => the io amount is used.
|
||||
///
|
||||
/// To check for unused io amount in stmts, we only consider `StmtKind::Semi`.
|
||||
/// `StmtKind::Local` is not considered because it binds values => the io amount is used.
|
||||
/// `StmtKind::Expr` is not considered because requires unit type => the io amount is used.
|
||||
/// `StmtKind::Item` is not considered because it's not an expression.
|
||||
///
|
||||
/// We then check the individual expressions via `check_expr`. We use the same logic for
|
||||
/// semi expressions and the final expression as we need to check match and if expressions
|
||||
/// for binding of the io amount to `Ok(_)`.
|
||||
///
|
||||
/// We explicitly check for the match source to be Normal as it needs special logic
|
||||
/// to consider the arms, and we want to avoid breaking the logic for situations where things
|
||||
/// get desugared to match.
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'tcx>) {
|
||||
for stmt in block.stmts {
|
||||
if let hir::StmtKind::Semi(exp) = stmt.kind {
|
||||
check_expr(cx, exp);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(exp) = block.expr
|
||||
&& matches!(exp.kind, hir::ExprKind::If(_, _, _) | hir::ExprKind::Match(_, _, _))
|
||||
{
|
||||
check_expr(cx, exp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) {
|
||||
match expr.kind {
|
||||
hir::ExprKind::If(cond, _, _)
|
||||
if let ExprKind::Let(hir::Let { pat, init, .. }) = cond.kind
|
||||
&& pattern_is_ignored_ok(cx, pat)
|
||||
&& let Some(op) = should_lint(cx, init) =>
|
||||
{
|
||||
emit_lint(cx, cond.span, op, &[pat.span]);
|
||||
},
|
||||
hir::ExprKind::Match(expr, arms, hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => {
|
||||
let found_arms: Vec<_> = arms
|
||||
.iter()
|
||||
.filter_map(|arm| {
|
||||
if pattern_is_ignored_ok(cx, arm.pat) {
|
||||
Some(arm.span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if !found_arms.is_empty() {
|
||||
emit_lint(cx, expr.span, op, found_arms.as_slice());
|
||||
}
|
||||
},
|
||||
_ if let Some(op) = should_lint(cx, expr) => {
|
||||
emit_lint(cx, expr.span, op, &[]);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
||||
fn should_lint<'a>(cx: &LateContext<'a>, mut inner: &'a hir::Expr<'a>) -> Option<IoOp> {
|
||||
inner = unpack_match(inner);
|
||||
inner = unpack_try(inner);
|
||||
inner = unpack_call_chain(inner);
|
||||
inner = unpack_await(inner);
|
||||
// we type-check it to get whether it's a read/write or their vectorized forms
|
||||
// and keep only the ones that are produce io amount
|
||||
check_io_mode(cx, inner)
|
||||
}
|
||||
|
||||
fn pattern_is_ignored_ok(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> bool {
|
||||
// the if checks whether we are in a result Ok( ) pattern
|
||||
// and the return checks whether it is unhandled
|
||||
|
||||
if let PatKind::TupleStruct(ref path, inner_pat, ddp) = pat.kind
|
||||
// we check against Result::Ok to avoid linting on Err(_) or something else.
|
||||
&& is_res_lang_ctor(cx, cx.qpath_res(path, pat.hir_id), hir::LangItem::ResultOk)
|
||||
{
|
||||
return match (inner_pat, ddp.as_opt_usize()) {
|
||||
// Ok(_) pattern
|
||||
([inner_pat], None) if matches!(inner_pat.kind, PatKind::Wild) => true,
|
||||
// Ok(..) pattern
|
||||
([], Some(0)) => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
|
||||
while let hir::ExprKind::MethodCall(path, receiver, ..) = expr.kind {
|
||||
if matches!(
|
||||
path.ident.as_str(),
|
||||
"unwrap" | "expect" | "unwrap_or" | "unwrap_or_else" | "ok" | "is_ok" | "is_err" | "or_else" | "or"
|
||||
) {
|
||||
expr = receiver;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
||||
fn unpack_try<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
|
||||
while let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind
|
||||
&& matches!(
|
||||
func.kind,
|
||||
hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..))
|
||||
)
|
||||
{
|
||||
expr = arg_0;
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
||||
fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
|
||||
while let hir::ExprKind::Match(res, _, _) = expr.kind {
|
||||
expr = res;
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
||||
/// If `expr` is an (e).await, return the inner expression "e" that's being
|
||||
/// waited on. Otherwise return None.
|
||||
fn try_remove_await<'a>(expr: &'a hir::Expr<'a>) -> Option<&hir::Expr<'a>> {
|
||||
fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &hir::Expr<'a> {
|
||||
if let hir::ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind {
|
||||
if let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind {
|
||||
if matches!(
|
||||
func.kind,
|
||||
hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..))
|
||||
) {
|
||||
return Some(arg_0);
|
||||
return arg_0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
expr
|
||||
}
|
||||
|
||||
fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
|
||||
let mut call = call;
|
||||
while let hir::ExprKind::MethodCall(path, receiver, ..) = call.kind {
|
||||
if matches!(path.ident.as_str(), "or" | "or_else" | "ok") {
|
||||
call = receiver;
|
||||
} else {
|
||||
break;
|
||||
/// Check whether the current expr is a function call for an IO operation
|
||||
fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option<IoOp> {
|
||||
let hir::ExprKind::MethodCall(path, ..) = call.kind else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let vectorized = match path.ident.as_str() {
|
||||
"write_vectored" | "read_vectored" => true,
|
||||
"write" | "read" => false,
|
||||
_ => {
|
||||
return None;
|
||||
},
|
||||
};
|
||||
|
||||
match (
|
||||
is_trait_method(cx, call, sym::IoRead),
|
||||
is_trait_method(cx, call, sym::IoWrite),
|
||||
match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT)
|
||||
|| match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT),
|
||||
match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT)
|
||||
|| match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT),
|
||||
) {
|
||||
(true, _, _, _) => Some(IoOp::SyncRead(vectorized)),
|
||||
(_, true, _, _) => Some(IoOp::SyncWrite(vectorized)),
|
||||
(_, _, true, _) => Some(IoOp::AsyncRead(vectorized)),
|
||||
(_, _, _, true) => Some(IoOp::AsyncWrite(vectorized)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_lint(cx: &LateContext<'_>, span: Span, op: IoOp, wild_cards: &[Span]) {
|
||||
let (msg, help) = match op {
|
||||
IoOp::AsyncRead(false) => (
|
||||
"read amount is not handled",
|
||||
Some("use `AsyncReadExt::read_exact` instead, or handle partial reads"),
|
||||
),
|
||||
IoOp::SyncRead(false) => (
|
||||
"read amount is not handled",
|
||||
Some("use `Read::read_exact` instead, or handle partial reads"),
|
||||
),
|
||||
IoOp::SyncWrite(false) => (
|
||||
"written amount is not handled",
|
||||
Some("use `Write::write_all` instead, or handle partial writes"),
|
||||
),
|
||||
IoOp::AsyncWrite(false) => (
|
||||
"written amount is not handled",
|
||||
Some("use `AsyncWriteExt::write_all` instead, or handle partial writes"),
|
||||
),
|
||||
IoOp::SyncRead(true) | IoOp::AsyncRead(true) => ("read amount is not handled", None),
|
||||
IoOp::SyncWrite(true) | IoOp::AsyncWrite(true) => ("written amount is not handled", None),
|
||||
};
|
||||
|
||||
span_lint_and_then(cx, UNUSED_IO_AMOUNT, span, msg, |diag| {
|
||||
if let Some(help_str) = help {
|
||||
diag.help(help_str);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(call) = try_remove_await(call) {
|
||||
check_method_call(cx, call, expr, true);
|
||||
} else {
|
||||
check_method_call(cx, call, expr, false);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>, is_await: bool) {
|
||||
if let hir::ExprKind::MethodCall(path, ..) = call.kind {
|
||||
let symbol = path.ident.as_str();
|
||||
let read_trait = if is_await {
|
||||
match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT)
|
||||
|| match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT)
|
||||
} else {
|
||||
is_trait_method(cx, call, sym::IoRead)
|
||||
};
|
||||
let write_trait = if is_await {
|
||||
match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT)
|
||||
|| match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT)
|
||||
} else {
|
||||
is_trait_method(cx, call, sym::IoWrite)
|
||||
};
|
||||
|
||||
match (read_trait, write_trait, symbol, is_await) {
|
||||
(true, _, "read", false) => span_lint_and_help(
|
||||
cx,
|
||||
UNUSED_IO_AMOUNT,
|
||||
expr.span,
|
||||
"read amount is not handled",
|
||||
None,
|
||||
"use `Read::read_exact` instead, or handle partial reads",
|
||||
),
|
||||
(true, _, "read", true) => span_lint_and_help(
|
||||
cx,
|
||||
UNUSED_IO_AMOUNT,
|
||||
expr.span,
|
||||
"read amount is not handled",
|
||||
None,
|
||||
"use `AsyncReadExt::read_exact` instead, or handle partial reads",
|
||||
),
|
||||
(true, _, "read_vectored", _) => {
|
||||
span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled");
|
||||
},
|
||||
(_, true, "write", false) => span_lint_and_help(
|
||||
cx,
|
||||
UNUSED_IO_AMOUNT,
|
||||
expr.span,
|
||||
"written amount is not handled",
|
||||
None,
|
||||
"use `Write::write_all` instead, or handle partial writes",
|
||||
),
|
||||
(_, true, "write", true) => span_lint_and_help(
|
||||
cx,
|
||||
UNUSED_IO_AMOUNT,
|
||||
expr.span,
|
||||
"written amount is not handled",
|
||||
None,
|
||||
"use `AsyncWriteExt::write_all` instead, or handle partial writes",
|
||||
),
|
||||
(_, true, "write_vectored", _) => {
|
||||
span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled");
|
||||
},
|
||||
_ => (),
|
||||
for span in wild_cards {
|
||||
diag.span_note(
|
||||
*span,
|
||||
"the result is consumed here, but the amount of I/O bytes remains unhandled",
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for AlmostStandardFormulation {
|
|||
ident.span,
|
||||
"non-standard lint formulation",
|
||||
None,
|
||||
&format!("try using `{}` instead", formulation.correction),
|
||||
&format!("consider using `{}`", formulation.correction),
|
||||
);
|
||||
}
|
||||
return;
|
||||
|
|
|
@ -71,7 +71,9 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
|
|||
SimplifiedType::Str,
|
||||
]
|
||||
.iter()
|
||||
.flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter().copied());
|
||||
.flat_map(|&ty| cx.tcx.incoherent_impls(ty).into_iter())
|
||||
.flatten()
|
||||
.copied();
|
||||
for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) {
|
||||
let lang_item_path = cx.get_def_path(item_def_id);
|
||||
if path_syms.starts_with(&lang_item_path) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use rustc_ast::{ast, attr};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_middle::ty::{AdtDef, TyCtxt};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::sym;
|
||||
use std::str::FromStr;
|
||||
|
@ -159,3 +160,14 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
|
|||
.filter_map(ast::Attribute::meta_item_list)
|
||||
.any(|l| attr::list_contains_name(&l, sym::hidden))
|
||||
}
|
||||
|
||||
pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool {
|
||||
adt.is_variant_list_non_exhaustive()
|
||||
|| tcx.has_attr(adt.did(), sym::non_exhaustive)
|
||||
|| adt.variants().iter().any(|variant_def| {
|
||||
variant_def.is_field_list_non_exhaustive() || tcx.has_attr(variant_def.def_id, sym::non_exhaustive)
|
||||
})
|
||||
|| adt
|
||||
.all_fields()
|
||||
.any(|field_def| tcx.has_attr(field_def.did, sym::non_exhaustive))
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
/// Checks whether two blocks are the same.
|
||||
#[expect(clippy::similar_names)]
|
||||
fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
|
||||
use TokenKind::{BlockComment, LineComment, Semi, Whitespace};
|
||||
use TokenKind::{Semi, Whitespace};
|
||||
if left.stmts.len() != right.stmts.len() {
|
||||
return false;
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
return false;
|
||||
}
|
||||
if !eq_span_tokens(self.inner.cx, lstart..lstmt_span.lo, rstart..rstmt_span.lo, |t| {
|
||||
!matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)
|
||||
!matches!(t, Whitespace | Semi)
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
return false;
|
||||
}
|
||||
eq_span_tokens(self.inner.cx, lstart..lend, rstart..rend, |t| {
|
||||
!matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi)
|
||||
!matches!(t, Whitespace | Semi)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -536,7 +536,12 @@ fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<It
|
|||
"f32" => SimplifiedType::Float(FloatTy::F32),
|
||||
"f64" => SimplifiedType::Float(FloatTy::F64),
|
||||
#[allow(trivial_casts)]
|
||||
_ => return Result::<_, rustc_errors::ErrorGuaranteed>::Ok(&[] as &[_]).into_iter().flatten().copied(),
|
||||
_ => {
|
||||
return Result::<_, rustc_errors::ErrorGuaranteed>::Ok(&[] as &[_])
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.copied();
|
||||
},
|
||||
};
|
||||
|
||||
tcx.incoherent_impls(ty).into_iter().flatten().copied()
|
||||
|
@ -1712,7 +1717,6 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
|
|||
PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable.
|
||||
PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
|
||||
PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
|
||||
PatKind::Lit(..) | PatKind::Range(..) => true,
|
||||
PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
|
||||
PatKind::Or(pats) => {
|
||||
// TODO: should be the honest check, that pats is exhaustive set
|
||||
|
@ -1736,7 +1740,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
|
|||
},
|
||||
}
|
||||
},
|
||||
PatKind::Err(_) => true,
|
||||
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
|
|||
pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"];
|
||||
pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
|
||||
pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
|
||||
pub const FILE_OPTIONS: [&str; 4] = ["std", "fs", "File", "options"];
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"];
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
|
@ -50,6 +51,7 @@ pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
|
|||
pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"];
|
||||
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
|
||||
pub const MSRV: [&str; 3] = ["clippy_config", "msrvs", "Msrv"];
|
||||
pub const OPEN_OPTIONS_NEW: [&str; 4] = ["std", "fs", "OpenOptions", "new"];
|
||||
pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
|
||||
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
|
||||
pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
|
||||
|
@ -89,9 +91,15 @@ pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol",
|
|||
pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
|
||||
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const TOKIO_FILE_OPTIONS: [&str; 5] = ["tokio", "fs", "file", "File", "options"];
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"];
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"];
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const TOKIO_IO_OPEN_OPTIONS: [&str; 4] = ["tokio", "fs", "open_options", "OpenOptions"];
|
||||
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const TOKIO_IO_OPEN_OPTIONS_NEW: [&str; 5] = ["tokio", "fs", "open_options", "OpenOptions", "new"];
|
||||
pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
|
||||
pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
|
||||
pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "iter"];
|
||||
|
|
|
@ -219,7 +219,8 @@ pub fn implements_trait<'tcx>(
|
|||
|
||||
/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
|
||||
///
|
||||
/// The `callee_id` argument is used to determine whether this is a function call in a `const fn` environment, used for checking const traits.
|
||||
/// The `callee_id` argument is used to determine whether this is a function call in a `const fn`
|
||||
/// environment, used for checking const traits.
|
||||
pub fn implements_trait_with_env<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "nightly-2024-01-11"
|
||||
channel = "nightly-2024-01-25"
|
||||
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
error: multiple versions for dependency `winapi`: 0.2.8, 0.3.9
|
||||
|
|
||||
= note: `-D clippy::multiple-crate-versions` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::multiple_crate_versions)]`
|
||||
|
||||
error: could not compile `multiple-crate-versions` (bin "multiple-crate-versions") due to 1 previous error
|
|
@ -0,0 +1,14 @@
|
|||
# Should not lint for dev or build dependencies. See issue 5041.
|
||||
|
||||
[package]
|
||||
# purposefully separated by - instead of _
|
||||
name = "multiple-crate-versions"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
|
||||
# One of the versions of winapi is only a dev dependency: allowed
|
||||
[dependencies]
|
||||
winapi = "0.2"
|
||||
ansi_term = "=0.11.0"
|
|
@ -0,0 +1,3 @@
|
|||
#![warn(clippy::multiple_crate_versions)]
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "multiple_crate_versions"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
winapi = "0.2"
|
||||
ansi_term = "=0.11.0"
|
|
@ -0,0 +1 @@
|
|||
allowed-duplicate-crates = ["winapi"]
|
|
@ -0,0 +1,3 @@
|
|||
#![warn(clippy::multiple_crate_versions)]
|
||||
|
||||
fn main() {}
|
|
@ -4,7 +4,7 @@ error: non-standard lint formulation
|
|||
LL | /// Check for lint formulations that are correct
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: try using `Checks for` instead
|
||||
= help: consider using `Checks for`
|
||||
= note: `-D clippy::almost-standard-lint-formulation` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::almost_standard_lint_formulation)]`
|
||||
|
||||
|
@ -14,7 +14,7 @@ error: non-standard lint formulation
|
|||
LL | /// Detects uses of incorrect formulations
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: try using `Checks for` instead
|
||||
= help: consider using `Checks for`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: use of a disallowed method `rustc_lint::context::LintContext::span_lint`
|
||||
--> $DIR/disallow_struct_span_lint.rs:14:5
|
||||
--> $DIR/disallow_span_lint.rs:14:5
|
||||
|
|
||||
LL | cx.span_lint(lint, span, msg, |_| {});
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -8,10 +8,10 @@ LL | cx.span_lint(lint, span, msg, |_| {});
|
|||
= help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]`
|
||||
|
||||
error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint`
|
||||
--> $DIR/disallow_struct_span_lint.rs:24:5
|
||||
--> $DIR/disallow_span_lint.rs:24:5
|
||||
|
|
||||
LL | tcx.node_span_lint(lint, hir_id, span, msg, |_| {});
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
pub-underscore-fields-behavior = "PublicallyExported"
|
||||
pub-underscore-fields-behavior = "PubliclyExported"
|
||||
|
|
|
@ -3,6 +3,9 @@ foobar = 42
|
|||
# so is this one
|
||||
barfoo = 53
|
||||
|
||||
# when using underscores instead of dashes, suggest the correct one
|
||||
allow_mixed_uninlined_format_args = true
|
||||
|
||||
# that one is ignored
|
||||
[third-party]
|
||||
clippy-feature = "nightly"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//@no-rustfix
|
||||
//@error-in-other-file: unknown field `foobar`, expected one of
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -11,6 +11,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
|
|||
allow-private-module-inception
|
||||
allow-unwrap-in-tests
|
||||
allowed-dotfiles
|
||||
allowed-duplicate-crates
|
||||
allowed-idents-below-min-chars
|
||||
allowed-scripts
|
||||
arithmetic-side-effects-allowed
|
||||
|
@ -87,6 +88,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
|
|||
allow-private-module-inception
|
||||
allow-unwrap-in-tests
|
||||
allowed-dotfiles
|
||||
allowed-duplicate-crates
|
||||
allowed-idents-below-min-chars
|
||||
allowed-scripts
|
||||
arithmetic-side-effects-allowed
|
||||
|
@ -150,5 +152,82 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
|
|||
LL | barfoo = 53
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: error reading Clippy's configuration file: unknown field `allow_mixed_uninlined_format_args`, expected one of
|
||||
absolute-paths-allowed-crates
|
||||
absolute-paths-max-segments
|
||||
accept-comment-above-attributes
|
||||
accept-comment-above-statement
|
||||
allow-dbg-in-tests
|
||||
allow-expect-in-tests
|
||||
allow-mixed-uninlined-format-args
|
||||
allow-one-hash-in-raw-strings
|
||||
allow-print-in-tests
|
||||
allow-private-module-inception
|
||||
allow-unwrap-in-tests
|
||||
allowed-dotfiles
|
||||
allowed-duplicate-crates
|
||||
allowed-idents-below-min-chars
|
||||
allowed-scripts
|
||||
arithmetic-side-effects-allowed
|
||||
arithmetic-side-effects-allowed-binary
|
||||
arithmetic-side-effects-allowed-unary
|
||||
array-size-threshold
|
||||
avoid-breaking-exported-api
|
||||
await-holding-invalid-types
|
||||
blacklisted-names
|
||||
cargo-ignore-publish
|
||||
check-private-items
|
||||
cognitive-complexity-threshold
|
||||
cyclomatic-complexity-threshold
|
||||
disallowed-macros
|
||||
disallowed-methods
|
||||
disallowed-names
|
||||
disallowed-types
|
||||
doc-valid-idents
|
||||
enable-raw-pointer-heuristic-for-send
|
||||
enforce-iter-loop-reborrow
|
||||
enforced-import-renames
|
||||
enum-variant-name-threshold
|
||||
enum-variant-size-threshold
|
||||
excessive-nesting-threshold
|
||||
future-size-threshold
|
||||
ignore-interior-mutability
|
||||
large-error-threshold
|
||||
literal-representation-threshold
|
||||
matches-for-let-else
|
||||
max-fn-params-bools
|
||||
max-include-file-size
|
||||
max-struct-bools
|
||||
max-suggested-slice-pattern-length
|
||||
max-trait-bounds
|
||||
min-ident-chars-threshold
|
||||
missing-docs-in-crate-items
|
||||
msrv
|
||||
pass-by-value-size-limit
|
||||
pub-underscore-fields-behavior
|
||||
semicolon-inside-block-ignore-singleline
|
||||
semicolon-outside-block-ignore-multiline
|
||||
single-char-binding-names-threshold
|
||||
stack-size-threshold
|
||||
standard-macro-braces
|
||||
struct-field-name-threshold
|
||||
suppress-restriction-lint-in-const
|
||||
third-party
|
||||
too-large-for-stack
|
||||
too-many-arguments-threshold
|
||||
too-many-lines-threshold
|
||||
trivial-copy-size-limit
|
||||
type-complexity-threshold
|
||||
unnecessary-box-size
|
||||
unreadable-literal-lint-fractions
|
||||
upper-case-acronyms-aggressive
|
||||
vec-box-size-threshold
|
||||
verbose-bit-mask-threshold
|
||||
warn-on-all-wildcard-imports
|
||||
--> $DIR/$DIR/clippy.toml:7:1
|
||||
|
|
||||
LL | allow_mixed_uninlined_format_args = true
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: perhaps you meant: `allow-mixed-uninlined-format-args`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ use quote::{quote, quote_spanned};
|
|||
use syn::spanned::Spanned;
|
||||
use syn::token::Star;
|
||||
use syn::{
|
||||
parse_macro_input, parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature,
|
||||
TraitItem, Type,
|
||||
parse_macro_input, parse_quote, FnArg, ImplItem, ItemFn, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType,
|
||||
Signature, TraitItem, Type,
|
||||
};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
|
@ -95,3 +95,33 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea
|
|||
|
||||
TokenStream::from(quote!(#item))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn fake_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let mut item = parse_macro_input!(item as ItemFn);
|
||||
let span = item.block.brace_token.span;
|
||||
|
||||
if item.sig.asyncness.is_some() {
|
||||
item.sig.asyncness = None;
|
||||
}
|
||||
|
||||
let crate_name = quote! { fake_crate };
|
||||
let block = item.block;
|
||||
item.block = syn::parse_quote_spanned! {
|
||||
span =>
|
||||
{
|
||||
#crate_name::block_on(async {
|
||||
#block
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
mod #crate_name {
|
||||
pub fn block_on<F: ::std::future::Future>(_fut: F) {}
|
||||
}
|
||||
|
||||
#item
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -85,4 +85,18 @@ fn block_in_match_expr(num: i32) -> i32 {
|
|||
}
|
||||
}
|
||||
|
||||
// issue #12162
|
||||
macro_rules! timed {
|
||||
($name:expr, $body:expr $(,)?) => {{
|
||||
let __scope = ();
|
||||
$body
|
||||
}};
|
||||
}
|
||||
|
||||
fn issue_12162() {
|
||||
if timed!("check this!", false) {
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -85,4 +85,18 @@ fn block_in_match_expr(num: i32) -> i32 {
|
|||
}
|
||||
}
|
||||
|
||||
// issue #12162
|
||||
macro_rules! timed {
|
||||
($name:expr, $body:expr $(,)?) => {{
|
||||
let __scope = ();
|
||||
$body
|
||||
}};
|
||||
}
|
||||
|
||||
fn issue_12162() {
|
||||
if timed!("check this!", false) {
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -9,17 +9,16 @@ fn simple_examples() {
|
|||
|
||||
// Simple
|
||||
if true {
|
||||
//~^ ERROR: all if blocks contain the same code at the start
|
||||
println!("Hello World!");
|
||||
println!("I'm branch nr: 1");
|
||||
} else {
|
||||
println!("Hello World!");
|
||||
println!("I'm branch nr: 2");
|
||||
}
|
||||
//~^^^^^^^ ERROR: all if blocks contain the same code at the start
|
||||
|
||||
// Else if
|
||||
if x == 0 {
|
||||
//~^ ERROR: all if blocks contain the same code at the start
|
||||
let y = 9;
|
||||
println!("The value y was set to: `{}`", y);
|
||||
let _z = y;
|
||||
|
@ -38,6 +37,7 @@ fn simple_examples() {
|
|||
|
||||
println!("Ha, Pascal allows you to start the array where you want")
|
||||
}
|
||||
//~^^^^^^^^^^^^^^^^^^^ ERROR: all if blocks contain the same code at the start
|
||||
|
||||
// Return a value
|
||||
let _ = if x == 7 {
|
||||
|
@ -60,7 +60,6 @@ fn simple_but_suggestion_is_invalid() {
|
|||
// Can't be automatically moved because used_value_name is getting used again
|
||||
let used_value_name = 19;
|
||||
if x == 10 {
|
||||
//~^ ERROR: all if blocks contain the same code at the start
|
||||
let used_value_name = "Different type";
|
||||
println!("Str: {}", used_value_name);
|
||||
let _ = 1;
|
||||
|
@ -69,6 +68,7 @@ fn simple_but_suggestion_is_invalid() {
|
|||
println!("Str: {}", used_value_name);
|
||||
let _ = 2;
|
||||
}
|
||||
//~^^^^^^^^^ ERROR: all if blocks contain the same code at the start
|
||||
let _ = used_value_name;
|
||||
|
||||
// This can be automatically moved as `can_be_overridden` is not used again
|
||||
|
@ -101,11 +101,11 @@ fn check_if_same_than_else_mask() {
|
|||
}
|
||||
|
||||
if x == 2019 {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
println!("This should trigger `IS_SAME_THAN_ELSE` as usual");
|
||||
} else {
|
||||
println!("This should trigger `IS_SAME_THAN_ELSE` as usual");
|
||||
}
|
||||
//~^^^^^ ERROR: this `if` has identical blocks
|
||||
}
|
||||
|
||||
#[allow(clippy::vec_init_then_push)]
|
||||
|
|
|
@ -2,7 +2,6 @@ error: all if blocks contain the same code at the start
|
|||
--> $DIR/shared_at_top.rs:11:5
|
||||
|
|
||||
LL | / if true {
|
||||
LL | |
|
||||
LL | | println!("Hello World!");
|
||||
| |_________________________________^
|
||||
|
|
||||
|
@ -21,7 +20,6 @@ error: all if blocks contain the same code at the start
|
|||
--> $DIR/shared_at_top.rs:21:5
|
||||
|
|
||||
LL | / if x == 0 {
|
||||
LL | |
|
||||
LL | | let y = 9;
|
||||
LL | | println!("The value y was set to: `{}`", y);
|
||||
LL | | let _z = y;
|
||||
|
@ -54,7 +52,6 @@ error: all if blocks contain the same code at the start
|
|||
--> $DIR/shared_at_top.rs:62:5
|
||||
|
|
||||
LL | / if x == 10 {
|
||||
LL | |
|
||||
LL | | let used_value_name = "Different type";
|
||||
LL | | println!("Str: {}", used_value_name);
|
||||
| |_____________________________________________^
|
||||
|
@ -105,13 +102,12 @@ error: this `if` has identical blocks
|
|||
|
|
||||
LL | if x == 2019 {
|
||||
| __________________^
|
||||
LL | |
|
||||
LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual");
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/shared_at_top.rs:106:12
|
||||
--> $DIR/shared_at_top.rs:105:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
|
|
@ -107,9 +107,9 @@ fn valid_examples() {
|
|||
|
||||
// Let's test empty blocks
|
||||
if false {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
} else {
|
||||
}
|
||||
//~^^^ ERROR: this `if` has identical blocks
|
||||
}
|
||||
|
||||
/// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint
|
||||
|
@ -119,7 +119,6 @@ fn trigger_other_lint() {
|
|||
|
||||
// Same block
|
||||
if x == 0 {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
let u = 19;
|
||||
println!("How are u today?");
|
||||
let _ = "This is a string";
|
||||
|
@ -128,6 +127,7 @@ fn trigger_other_lint() {
|
|||
println!("How are u today?");
|
||||
let _ = "This is a string";
|
||||
}
|
||||
//~^^^^^^^^^ ERROR: this `if` has identical blocks
|
||||
|
||||
// Only same expression
|
||||
let _ = if x == 6 { 7 } else { 7 };
|
||||
|
@ -138,28 +138,24 @@ fn trigger_other_lint() {
|
|||
println!("Well I'm the most important block");
|
||||
"I'm a pretty string"
|
||||
} else if x == 68 {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
println!("I'm a doppelgänger");
|
||||
// Don't listen to my clone below
|
||||
|
||||
if y == 90 { "=^.^=" } else { ":D" }
|
||||
} else {
|
||||
// Don't listen to my clone above
|
||||
println!("I'm a doppelgänger");
|
||||
|
||||
if y == 90 { "=^.^=" } else { ":D" }
|
||||
};
|
||||
//~^^^^^^^^^ ERROR: this `if` has identical blocks
|
||||
|
||||
if x == 0 {
|
||||
println!("I'm single");
|
||||
} else if x == 68 {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
println!("I'm a doppelgänger");
|
||||
// Don't listen to my clone below
|
||||
} else {
|
||||
// Don't listen to my clone above
|
||||
println!("I'm a doppelgänger");
|
||||
}
|
||||
//~^^^^^ ERROR: this `if` has identical blocks
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -3,12 +3,11 @@ error: this `if` has identical blocks
|
|||
|
|
||||
LL | if false {
|
||||
| ______________^
|
||||
LL | |
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/valid_if_blocks.rs:111:12
|
||||
--> $DIR/valid_if_blocks.rs:110:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -25,7 +24,6 @@ error: this `if` has identical blocks
|
|||
|
|
||||
LL | if x == 0 {
|
||||
| _______________^
|
||||
LL | |
|
||||
LL | | let u = 19;
|
||||
LL | | println!("How are u today?");
|
||||
LL | | let _ = "This is a string";
|
||||
|
@ -33,7 +31,7 @@ LL | | } else {
|
|||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/valid_if_blocks.rs:126:12
|
||||
--> $DIR/valid_if_blocks.rs:125:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -60,20 +58,17 @@ error: this `if` has identical blocks
|
|||
|
|
||||
LL | } else if x == 68 {
|
||||
| _______________________^
|
||||
LL | |
|
||||
LL | | println!("I'm a doppelgänger");
|
||||
LL | | // Don't listen to my clone below
|
||||
LL | |
|
||||
LL | | if y == 90 { "=^.^=" } else { ":D" }
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/valid_if_blocks.rs:146:12
|
||||
--> $DIR/valid_if_blocks.rs:144:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
LL | | // Don't listen to my clone above
|
||||
LL | | println!("I'm a doppelgänger");
|
||||
LL | |
|
||||
LL | | if y == 90 { "=^.^=" } else { ":D" }
|
||||
|
@ -81,22 +76,19 @@ LL | | };
|
|||
| |_____^
|
||||
|
||||
error: this `if` has identical blocks
|
||||
--> $DIR/valid_if_blocks.rs:155:23
|
||||
--> $DIR/valid_if_blocks.rs:153:23
|
||||
|
|
||||
LL | } else if x == 68 {
|
||||
| _______________________^
|
||||
LL | |
|
||||
LL | | println!("I'm a doppelgänger");
|
||||
LL | | // Don't listen to my clone below
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/valid_if_blocks.rs:159:12
|
||||
--> $DIR/valid_if_blocks.rs:155:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
LL | | // Don't listen to my clone above
|
||||
LL | | println!("I'm a doppelgänger");
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
|
@ -2,7 +2,7 @@ error: called `is_some()` after searching an `Iterator` with `find`
|
|||
--> $DIR/ice-9041.rs:5:19
|
||||
|
|
||||
LL | things.iter().find(|p| is_thing_ready(p)).is_some()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|p| is_thing_ready(&p))`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|p| is_thing_ready(&p))`
|
||||
|
|
||||
= note: `-D clippy::search-is-some` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::search_is_some)]`
|
||||
|
|
28
tests/ui/default_instead_of_iter_empty_no_std.fixed
Normal file
28
tests/ui/default_instead_of_iter_empty_no_std.fixed
Normal file
|
@ -0,0 +1,28 @@
|
|||
#![warn(clippy::default_instead_of_iter_empty)]
|
||||
#![allow(dead_code)]
|
||||
#![feature(lang_items)]
|
||||
#![no_std]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
extern "C" fn eh_personality() {}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Iter {
|
||||
iter: core::iter::Empty<usize>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Do lint.
|
||||
let _ = core::iter::empty::<usize>();
|
||||
let _foo: core::iter::Empty<usize> = core::iter::empty();
|
||||
|
||||
// Do not lint.
|
||||
let _ = Iter::default();
|
||||
}
|
28
tests/ui/default_instead_of_iter_empty_no_std.rs
Normal file
28
tests/ui/default_instead_of_iter_empty_no_std.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
#![warn(clippy::default_instead_of_iter_empty)]
|
||||
#![allow(dead_code)]
|
||||
#![feature(lang_items)]
|
||||
#![no_std]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
extern "C" fn eh_personality() {}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Iter {
|
||||
iter: core::iter::Empty<usize>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Do lint.
|
||||
let _ = core::iter::Empty::<usize>::default();
|
||||
let _foo: core::iter::Empty<usize> = core::iter::Empty::default();
|
||||
|
||||
// Do not lint.
|
||||
let _ = Iter::default();
|
||||
}
|
17
tests/ui/default_instead_of_iter_empty_no_std.stderr
Normal file
17
tests/ui/default_instead_of_iter_empty_no_std.stderr
Normal file
|
@ -0,0 +1,17 @@
|
|||
error: `core::iter::empty()` is the more idiomatic way
|
||||
--> $DIR/default_instead_of_iter_empty_no_std.rs:23:13
|
||||
|
|
||||
LL | let _ = core::iter::Empty::<usize>::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::iter::empty::<usize>()`
|
||||
|
|
||||
= note: `-D clippy::default-instead-of-iter-empty` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::default_instead_of_iter_empty)]`
|
||||
|
||||
error: `core::iter::empty()` is the more idiomatic way
|
||||
--> $DIR/default_instead_of_iter_empty_no_std.rs:24:42
|
||||
|
|
||||
LL | let _foo: core::iter::Empty<usize> = core::iter::Empty::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::iter::empty()`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -216,4 +216,44 @@ mod type_already_inferred {
|
|||
}
|
||||
}
|
||||
|
||||
mod issue12159 {
|
||||
#![allow(non_upper_case_globals, clippy::exhaustive_structs)]
|
||||
pub struct Foo;
|
||||
|
||||
static F: i32 = 1;
|
||||
impl Foo {
|
||||
const LIFE_u8: u8 = 42;
|
||||
const LIFE_i8: i8 = 42;
|
||||
const LIFE_u16: u16 = 42;
|
||||
const LIFE_i16: i16 = 42;
|
||||
const LIFE_u32: u32 = 42;
|
||||
const LIFE_i32: i32 = 42;
|
||||
const LIFE_u64: u64 = 42;
|
||||
const LIFE_i64: i64 = 42;
|
||||
const LIFE_u128: u128 = 42;
|
||||
const LIFE_i128: i128 = 42;
|
||||
const LIFE_usize: usize = 42;
|
||||
const LIFE_isize: isize = 42;
|
||||
const LIFE_f32: f32 = 42.;
|
||||
const LIFE_f64: f64 = 42.;
|
||||
|
||||
const fn consts() {
|
||||
const LIFE_u8: u8 = 42;
|
||||
const LIFE_i8: i8 = 42;
|
||||
const LIFE_u16: u16 = 42;
|
||||
const LIFE_i16: i16 = 42;
|
||||
const LIFE_u32: u32 = 42;
|
||||
const LIFE_i32: i32 = 42;
|
||||
const LIFE_u64: u64 = 42;
|
||||
const LIFE_i64: i64 = 42;
|
||||
const LIFE_u128: u128 = 42;
|
||||
const LIFE_i128: i128 = 42;
|
||||
const LIFE_usize: usize = 42;
|
||||
const LIFE_isize: isize = 42;
|
||||
const LIFE_f32: f32 = 42.;
|
||||
const LIFE_f64: f64 = 42.;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -216,4 +216,44 @@ mod type_already_inferred {
|
|||
}
|
||||
}
|
||||
|
||||
mod issue12159 {
|
||||
#![allow(non_upper_case_globals, clippy::exhaustive_structs)]
|
||||
pub struct Foo;
|
||||
|
||||
static F: i32 = 1;
|
||||
impl Foo {
|
||||
const LIFE_u8: u8 = 42;
|
||||
const LIFE_i8: i8 = 42;
|
||||
const LIFE_u16: u16 = 42;
|
||||
const LIFE_i16: i16 = 42;
|
||||
const LIFE_u32: u32 = 42;
|
||||
const LIFE_i32: i32 = 42;
|
||||
const LIFE_u64: u64 = 42;
|
||||
const LIFE_i64: i64 = 42;
|
||||
const LIFE_u128: u128 = 42;
|
||||
const LIFE_i128: i128 = 42;
|
||||
const LIFE_usize: usize = 42;
|
||||
const LIFE_isize: isize = 42;
|
||||
const LIFE_f32: f32 = 42.;
|
||||
const LIFE_f64: f64 = 42.;
|
||||
|
||||
const fn consts() {
|
||||
const LIFE_u8: u8 = 42;
|
||||
const LIFE_i8: i8 = 42;
|
||||
const LIFE_u16: u16 = 42;
|
||||
const LIFE_i16: i16 = 42;
|
||||
const LIFE_u32: u32 = 42;
|
||||
const LIFE_i32: i32 = 42;
|
||||
const LIFE_u64: u64 = 42;
|
||||
const LIFE_i64: i64 = 42;
|
||||
const LIFE_u128: u128 = 42;
|
||||
const LIFE_i128: i128 = 42;
|
||||
const LIFE_usize: usize = 42;
|
||||
const LIFE_isize: isize = 42;
|
||||
const LIFE_f32: f32 = 42.;
|
||||
const LIFE_f64: f64 = 42.;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -121,4 +121,36 @@ pub fn _from_mod() -> _hidden::InPubFn {
|
|||
#[derive(PartialEq)]
|
||||
struct InternalTy;
|
||||
|
||||
// This is a `non_exhaustive` type so should not warn.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub struct MissingEqNonExhaustive {
|
||||
foo: u32,
|
||||
bar: String,
|
||||
}
|
||||
|
||||
// This is a `non_exhaustive` type so should not warn.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct MissingEqNonExhaustive1 {
|
||||
foo: u32,
|
||||
#[non_exhaustive]
|
||||
bar: String,
|
||||
}
|
||||
|
||||
// This is a `non_exhaustive` type so should not warn.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum MissingEqNonExhaustive2 {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
// This is a `non_exhaustive` type so should not warn.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum MissingEqNonExhaustive3 {
|
||||
Foo,
|
||||
#[non_exhaustive]
|
||||
Bar,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -121,4 +121,36 @@ pub fn _from_mod() -> _hidden::InPubFn {
|
|||
#[derive(PartialEq)]
|
||||
struct InternalTy;
|
||||
|
||||
// This is a `non_exhaustive` type so should not warn.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub struct MissingEqNonExhaustive {
|
||||
foo: u32,
|
||||
bar: String,
|
||||
}
|
||||
|
||||
// This is a `non_exhaustive` type so should not warn.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct MissingEqNonExhaustive1 {
|
||||
foo: u32,
|
||||
#[non_exhaustive]
|
||||
bar: String,
|
||||
}
|
||||
|
||||
// This is a `non_exhaustive` type so should not warn.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum MissingEqNonExhaustive2 {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
// This is a `non_exhaustive` type so should not warn.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum MissingEqNonExhaustive3 {
|
||||
Foo,
|
||||
#[non_exhaustive]
|
||||
Bar,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -64,7 +64,7 @@ fn test_units() {
|
|||
/// NaN NaNs
|
||||
/// OAuth GraphQL
|
||||
/// OCaml
|
||||
/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS
|
||||
/// OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry
|
||||
/// WebGL WebGL2 WebGPU
|
||||
/// TensorFlow
|
||||
/// TrueType
|
||||
|
|
|
@ -64,7 +64,7 @@ fn test_units() {
|
|||
/// NaN NaNs
|
||||
/// OAuth GraphQL
|
||||
/// OCaml
|
||||
/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS
|
||||
/// OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry
|
||||
/// WebGL WebGL2 WebGPU
|
||||
/// TensorFlow
|
||||
/// TrueType
|
||||
|
|
|
@ -89,4 +89,12 @@ fn msrv_1_41() {
|
|||
}
|
||||
}
|
||||
|
||||
fn issue_12138() {
|
||||
struct Hello;
|
||||
|
||||
impl From<Hello> for () {
|
||||
fn from(val: Hello) {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -89,4 +89,12 @@ fn msrv_1_41() {
|
|||
}
|
||||
}
|
||||
|
||||
fn issue_12138() {
|
||||
struct Hello;
|
||||
|
||||
impl Into<()> for Hello {
|
||||
fn into(self) {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -86,5 +86,19 @@ LL ~ fn from(val: Vec<T>) -> Self {
|
|||
LL ~ FromOverInto(val)
|
||||
|
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
|
||||
--> $DIR/from_over_into.rs:95:5
|
||||
|
|
||||
LL | impl Into<()> for Hello {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: `impl From<Local> for Foreign` is allowed by the orphan rules, for more information see
|
||||
https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence
|
||||
help: replace the `Into` implementation with `From<issue_12138::Hello>`
|
||||
|
|
||||
LL ~ impl From<Hello> for () {
|
||||
LL ~ fn from(val: Hello) {}
|
||||
|
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ fn foo() -> bool {
|
|||
|
||||
fn if_same_then_else() {
|
||||
if true {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
Foo { bar: 42 };
|
||||
0..10;
|
||||
..;
|
||||
|
@ -38,6 +37,7 @@ fn if_same_then_else() {
|
|||
0..=10;
|
||||
foo();
|
||||
}
|
||||
//~^^^^^^^^^^^^^^^^^ ERROR: this `if` has identical blocks
|
||||
|
||||
if true {
|
||||
Foo { bar: 42 };
|
||||
|
@ -64,19 +64,11 @@ fn if_same_then_else() {
|
|||
foo();
|
||||
}
|
||||
|
||||
let _ = if true {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
0.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let _ = if true { 0.0 } else { 0.0 };
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
|
||||
let _ = if true {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
-0.0
|
||||
} else {
|
||||
-0.0
|
||||
};
|
||||
let _ = if true { -0.0 } else { -0.0 };
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
|
||||
let _ = if true { 0.0 } else { -0.0 };
|
||||
|
||||
|
@ -87,15 +79,10 @@ fn if_same_then_else() {
|
|||
foo();
|
||||
}
|
||||
|
||||
let _ = if true {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
42
|
||||
} else {
|
||||
42
|
||||
};
|
||||
let _ = if true { 42 } else { 42 };
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
|
||||
if true {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
let bar = if true { 42 } else { 43 };
|
||||
|
||||
while foo() {
|
||||
|
@ -110,6 +97,7 @@ fn if_same_then_else() {
|
|||
}
|
||||
bar + 1;
|
||||
}
|
||||
//~^^^^^^^^^^^^^^^ ERROR: this `if` has identical blocks
|
||||
|
||||
if true {
|
||||
let _ = match 42 {
|
||||
|
|
|
@ -3,16 +3,16 @@ error: this `if` has identical blocks
|
|||
|
|
||||
LL | if true {
|
||||
| _____________^
|
||||
LL | |
|
||||
LL | | Foo { bar: 42 };
|
||||
LL | | 0..10;
|
||||
LL | | ..;
|
||||
... |
|
||||
LL | | foo();
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else.rs:32:12
|
||||
--> $DIR/if_same_then_else.rs:31:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -29,75 +29,54 @@ LL | | }
|
|||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else.rs:67:21
|
||||
|
|
||||
LL | let _ = if true {
|
||||
| _____________________^
|
||||
LL | |
|
||||
LL | | 0.0
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
LL | let _ = if true { 0.0 } else { 0.0 };
|
||||
| ^^^^^^^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else.rs:70:12
|
||||
--> $DIR/if_same_then_else.rs:67:34
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
LL | | 0.0
|
||||
LL | | };
|
||||
| |_____^
|
||||
LL | let _ = if true { 0.0 } else { 0.0 };
|
||||
| ^^^^^^^
|
||||
|
||||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else.rs:74:21
|
||||
--> $DIR/if_same_then_else.rs:70:21
|
||||
|
|
||||
LL | let _ = if true {
|
||||
| _____________________^
|
||||
LL | |
|
||||
LL | | -0.0
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
LL | let _ = if true { -0.0 } else { -0.0 };
|
||||
| ^^^^^^^^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else.rs:77:12
|
||||
--> $DIR/if_same_then_else.rs:70:35
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
LL | | -0.0
|
||||
LL | | };
|
||||
| |_____^
|
||||
LL | let _ = if true { -0.0 } else { -0.0 };
|
||||
| ^^^^^^^^
|
||||
|
||||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else.rs:90:21
|
||||
--> $DIR/if_same_then_else.rs:82:21
|
||||
|
|
||||
LL | let _ = if true {
|
||||
| _____________________^
|
||||
LL | |
|
||||
LL | | 42
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
LL | let _ = if true { 42 } else { 42 };
|
||||
| ^^^^^^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else.rs:93:12
|
||||
--> $DIR/if_same_then_else.rs:82:33
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
LL | | 42
|
||||
LL | | };
|
||||
| |_____^
|
||||
LL | let _ = if true { 42 } else { 42 };
|
||||
| ^^^^^^
|
||||
|
||||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else.rs:97:13
|
||||
--> $DIR/if_same_then_else.rs:85:13
|
||||
|
|
||||
LL | if true {
|
||||
| _____________^
|
||||
LL | |
|
||||
LL | | let bar = if true { 42 } else { 43 };
|
||||
LL | |
|
||||
LL | | while foo() {
|
||||
... |
|
||||
LL | | bar + 1;
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else.rs:105:12
|
||||
--> $DIR/if_same_then_else.rs:92:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -110,7 +89,7 @@ LL | | }
|
|||
| |_____^
|
||||
|
||||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else.rs:250:14
|
||||
--> $DIR/if_same_then_else.rs:238:14
|
||||
|
|
||||
LL | if x {
|
||||
| ______________^
|
||||
|
@ -119,7 +98,7 @@ LL | | } else {
|
|||
| |_________^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else.rs:252:16
|
||||
--> $DIR/if_same_then_else.rs:240:16
|
||||
|
|
||||
LL | } else {
|
||||
| ________________^
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
fn if_same_then_else2() -> Result<&'static str, ()> {
|
||||
if true {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
for _ in &[42] {
|
||||
let foo: &Option<_> = &Some::<u8>(42);
|
||||
if foo.is_some() {
|
||||
|
@ -32,20 +31,21 @@ fn if_same_then_else2() -> Result<&'static str, ()> {
|
|||
}
|
||||
}
|
||||
}
|
||||
//~^^^^^^^^^^^^^^^^^^^ ERROR: this `if` has identical blocks
|
||||
|
||||
if true {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
if let Some(a) = Some(42) {}
|
||||
} else {
|
||||
if let Some(a) = Some(42) {}
|
||||
}
|
||||
//~^^^^^ ERROR: this `if` has identical blocks
|
||||
|
||||
if true {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
if let (1, .., 3) = (1, 2, 3) {}
|
||||
} else {
|
||||
if let (1, .., 3) = (1, 2, 3) {}
|
||||
}
|
||||
//~^^^^^ ERROR: this `if` has identical blocks
|
||||
|
||||
if true {
|
||||
if let (1, .., 3) = (1, 2, 3) {}
|
||||
|
@ -90,19 +90,15 @@ fn if_same_then_else2() -> Result<&'static str, ()> {
|
|||
}
|
||||
|
||||
// Same NaNs
|
||||
let _ = if true {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
f32::NAN
|
||||
} else {
|
||||
f32::NAN
|
||||
};
|
||||
let _ = if true { f32::NAN } else { f32::NAN };
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
|
||||
if true {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
Ok("foo")?;
|
||||
} else {
|
||||
Ok("foo")?;
|
||||
}
|
||||
//~^^^^^ ERROR: this `if` has identical blocks
|
||||
|
||||
if true {
|
||||
let foo = "";
|
||||
|
@ -122,13 +118,13 @@ fn if_same_then_else2() -> Result<&'static str, ()> {
|
|||
let foo = "bar";
|
||||
return Ok(&foo[0..]);
|
||||
} else if true {
|
||||
//~^ ERROR: this `if` has identical blocks
|
||||
let foo = "";
|
||||
return Ok(&foo[0..]);
|
||||
} else {
|
||||
let foo = "";
|
||||
return Ok(&foo[0..]);
|
||||
}
|
||||
//~^^^^^^^ ERROR: this `if` has identical blocks
|
||||
|
||||
// False positive `if_same_then_else`: `let (x, y)` vs. `let (y, x)`; see issue #3559.
|
||||
if true {
|
||||
|
|
|
@ -3,16 +3,16 @@ error: this `if` has identical blocks
|
|||
|
|
||||
LL | if true {
|
||||
| _____________^
|
||||
LL | |
|
||||
LL | | for _ in &[42] {
|
||||
LL | | let foo: &Option<_> = &Some::<u8>(42);
|
||||
LL | | if foo.is_some() {
|
||||
... |
|
||||
LL | | }
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else2.rs:25:12
|
||||
--> $DIR/if_same_then_else2.rs:24:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -31,13 +31,12 @@ error: this `if` has identical blocks
|
|||
|
|
||||
LL | if true {
|
||||
| _____________^
|
||||
LL | |
|
||||
LL | | if let Some(a) = Some(42) {}
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else2.rs:39:12
|
||||
--> $DIR/if_same_then_else2.rs:38:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -50,13 +49,12 @@ error: this `if` has identical blocks
|
|||
|
|
||||
LL | if true {
|
||||
| _____________^
|
||||
LL | |
|
||||
LL | | if let (1, .., 3) = (1, 2, 3) {}
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else2.rs:46:12
|
||||
--> $DIR/if_same_then_else2.rs:45:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -67,34 +65,26 @@ LL | | }
|
|||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else2.rs:93:21
|
||||
|
|
||||
LL | let _ = if true {
|
||||
| _____________________^
|
||||
LL | |
|
||||
LL | | f32::NAN
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
LL | let _ = if true { f32::NAN } else { f32::NAN };
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else2.rs:96:12
|
||||
--> $DIR/if_same_then_else2.rs:93:39
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
LL | | f32::NAN
|
||||
LL | | };
|
||||
| |_____^
|
||||
LL | let _ = if true { f32::NAN } else { f32::NAN };
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else2.rs:100:13
|
||||
--> $DIR/if_same_then_else2.rs:96:13
|
||||
|
|
||||
LL | if true {
|
||||
| _____________^
|
||||
LL | |
|
||||
LL | | Ok("foo")?;
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else2.rs:103:12
|
||||
--> $DIR/if_same_then_else2.rs:98:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
@ -103,18 +93,17 @@ LL | | }
|
|||
| |_____^
|
||||
|
||||
error: this `if` has identical blocks
|
||||
--> $DIR/if_same_then_else2.rs:124:20
|
||||
--> $DIR/if_same_then_else2.rs:120:20
|
||||
|
|
||||
LL | } else if true {
|
||||
| ____________________^
|
||||
LL | |
|
||||
LL | | let foo = "";
|
||||
LL | | return Ok(&foo[0..]);
|
||||
LL | | } else {
|
||||
| |_____^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/if_same_then_else2.rs:128:12
|
||||
--> $DIR/if_same_then_else2.rs:123:12
|
||||
|
|
||||
LL | } else {
|
||||
| ____________^
|
||||
|
|
|
@ -26,16 +26,23 @@ fn main() {
|
|||
.unwrap();
|
||||
let file = OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.append(false)
|
||||
.open("dump.json")
|
||||
.unwrap();
|
||||
let file = OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(false)
|
||||
.append(false)
|
||||
.open("dump.json")
|
||||
.unwrap();
|
||||
let file = OpenOptions::new().create(true).append(true).open("dump.json").unwrap();
|
||||
let file = OpenOptions::new().create(true).write(true).open("dump.json").unwrap();
|
||||
let file = OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open("dump.json")
|
||||
.unwrap();
|
||||
}
|
||||
|
|
|
@ -26,16 +26,23 @@ fn main() {
|
|||
.unwrap();
|
||||
let file = OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.append(false)
|
||||
.open("dump.json")
|
||||
.unwrap();
|
||||
let file = OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(false)
|
||||
.append(false)
|
||||
.open("dump.json")
|
||||
.unwrap();
|
||||
let file = OpenOptions::new().create(true).append(true).open("dump.json").unwrap();
|
||||
let file = OpenOptions::new().create(true).write(true).open("dump.json").unwrap();
|
||||
let file = OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open("dump.json")
|
||||
.unwrap();
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ help: if this is unintentional, try removing the starting separator
|
|||
|
|
||||
LL | path.join("sh");
|
||||
| ~~~~
|
||||
help: if this is intentional, try using `Path::new` instead
|
||||
help: if this is intentional, consider using `Path::new`
|
||||
|
|
||||
LL | PathBuf::from("/sh");
|
||||
| ~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -27,7 +27,7 @@ help: if this is unintentional, try removing the starting separator
|
|||
|
|
||||
LL | path.join("\user");
|
||||
| ~~~~~~~
|
||||
help: if this is intentional, try using `Path::new` instead
|
||||
help: if this is intentional, consider using `Path::new`
|
||||
|
|
||||
LL | PathBuf::from("\\user");
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -43,7 +43,7 @@ help: if this is unintentional, try removing the starting separator
|
|||
|
|
||||
LL | path.join("sh");
|
||||
| ~~~~
|
||||
help: if this is intentional, try using `Path::new` instead
|
||||
help: if this is intentional, consider using `Path::new`
|
||||
|
|
||||
LL | PathBuf::from("/sh");
|
||||
| ~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -59,7 +59,7 @@ help: if this is unintentional, try removing the starting separator
|
|||
|
|
||||
LL | path.join(r#"sh"#);
|
||||
| ~~~~~~~
|
||||
help: if this is intentional, try using `Path::new` instead
|
||||
help: if this is intentional, consider using `Path::new`
|
||||
|
|
||||
LL | PathBuf::from(r#"/sh"#);
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -17,7 +17,7 @@ error: called `map_or(Err(_), Ok)` on an `Option` value
|
|||
--> $DIR/manual_ok_or.rs:14:5
|
||||
|
|
||||
LL | foo.map_or(Err("error"), Ok);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok_or` instead: `foo.ok_or("error")`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `ok_or`: `foo.ok_or("error")`
|
||||
|
|
||||
= note: `-D clippy::option-map-or-err-ok` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::option_map_or_err_ok)]`
|
||||
|
|
|
@ -2,7 +2,7 @@ error: manual saturating arithmetic
|
|||
--> $DIR/manual_saturating_arithmetic.rs:6:13
|
||||
|
|
||||
LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)`
|
||||
|
|
||||
= note: `-D clippy::manual-saturating-arithmetic` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::manual_saturating_arithmetic)]`
|
||||
|
@ -11,13 +11,13 @@ error: manual saturating arithmetic
|
|||
--> $DIR/manual_saturating_arithmetic.rs:7:13
|
||||
|
|
||||
LL | let _ = 1u32.checked_add(1).unwrap_or(u32::MAX);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:8:13
|
||||
|
|
||||
LL | let _ = 1u8.checked_add(1).unwrap_or(255);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u8.saturating_add(1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u8.saturating_add(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:9:13
|
||||
|
@ -26,49 +26,49 @@ LL | let _ = 1u128
|
|||
| _____________^
|
||||
LL | | .checked_add(1)
|
||||
LL | | .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455);
|
||||
| |_______________________________________________________________________^ help: try using `saturating_add`: `1u128.saturating_add(1)`
|
||||
| |_______________________________________________________________________^ help: consider using `saturating_add`: `1u128.saturating_add(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:14:13
|
||||
|
|
||||
LL | let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_mul`: `1u32.saturating_mul(1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_mul`: `1u32.saturating_mul(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:16:13
|
||||
|
|
||||
LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:17:13
|
||||
|
|
||||
LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:18:13
|
||||
|
|
||||
LL | let _ = 1u8.checked_sub(1).unwrap_or(0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u8.saturating_sub(1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u8.saturating_sub(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:22:13
|
||||
|
|
||||
LL | let _ = 1i32.checked_add(1).unwrap_or(i32::max_value());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:23:13
|
||||
|
|
||||
LL | let _ = 1i32.checked_add(1).unwrap_or(i32::MAX);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:24:13
|
||||
|
|
||||
LL | let _ = 1i8.checked_add(1).unwrap_or(127);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:25:13
|
||||
|
@ -77,25 +77,25 @@ LL | let _ = 1i128
|
|||
| _____________^
|
||||
LL | | .checked_add(1)
|
||||
LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
|
||||
| |_______________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(1)`
|
||||
| |_______________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:28:13
|
||||
|
|
||||
LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:29:13
|
||||
|
|
||||
LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:30:13
|
||||
|
|
||||
LL | let _ = 1i8.checked_add(-1).unwrap_or(-128);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(-1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(-1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:31:13
|
||||
|
@ -104,25 +104,25 @@ LL | let _ = 1i128
|
|||
| _____________^
|
||||
LL | | .checked_add(-1)
|
||||
LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
|
||||
| |________________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(-1)`
|
||||
| |________________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(-1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:38:13
|
||||
|
|
||||
LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:39:13
|
||||
|
|
||||
LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:40:13
|
||||
|
|
||||
LL | let _ = 1i8.checked_sub(1).unwrap_or(-128);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:41:13
|
||||
|
@ -131,25 +131,25 @@ LL | let _ = 1i128
|
|||
| _____________^
|
||||
LL | | .checked_sub(1)
|
||||
LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
|
||||
| |________________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(1)`
|
||||
| |________________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:44:13
|
||||
|
|
||||
LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:45:13
|
||||
|
|
||||
LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:46:13
|
||||
|
|
||||
LL | let _ = 1i8.checked_sub(-1).unwrap_or(127);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(-1)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(-1)`
|
||||
|
||||
error: manual saturating arithmetic
|
||||
--> $DIR/manual_saturating_arithmetic.rs:47:13
|
||||
|
@ -158,7 +158,7 @@ LL | let _ = 1i128
|
|||
| _____________^
|
||||
LL | | .checked_sub(-1)
|
||||
LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
|
||||
| |_______________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(-1)`
|
||||
| |_______________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(-1)`
|
||||
|
||||
error: aborting due to 24 previous errors
|
||||
|
||||
|
|
|
@ -69,15 +69,48 @@ fn main() {
|
|||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
let y = x.cloned();
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `cloned` method
|
||||
let y = x.cloned();
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `cloned` method
|
||||
|
||||
let x: Option<u32> = Some(0);
|
||||
let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint.
|
||||
let y = x.copied();
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `copied` method
|
||||
let y = x.copied();
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `copied` method
|
||||
|
||||
// Should not suggest `copied` or `cloned` here since `T` is not a reference.
|
||||
let x: Option<u32> = Some(0);
|
||||
let y = x.map(|x| u32::clone(&x));
|
||||
let y = x.map(|x| Clone::clone(&x));
|
||||
|
||||
// Testing with `Result` now.
|
||||
let x: Result<String, ()> = Ok(String::new());
|
||||
let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint.
|
||||
let y = x.cloned();
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `cloned` method
|
||||
let y = x.cloned();
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `cloned` method
|
||||
|
||||
let x: Result<u32, ()> = Ok(0);
|
||||
let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint.
|
||||
let y = x.copied();
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `copied` method
|
||||
let y = x.copied();
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `copied` method
|
||||
|
||||
// Should not suggest `copied` or `cloned` here since `T` is not a reference.
|
||||
let x: Result<u32, ()> = Ok(0);
|
||||
let y = x.map(|x| u32::clone(&x));
|
||||
let y = x.map(|x| Clone::clone(&x));
|
||||
|
||||
// We ensure that no warning is emitted here because `useless_asref` is taking over.
|
||||
let x = Some(String::new());
|
||||
|
|
|
@ -69,15 +69,48 @@ fn main() {
|
|||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
let y = x.map(Clone::clone);
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `cloned` method
|
||||
let y = x.map(String::clone);
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `cloned` method
|
||||
|
||||
let x: Option<u32> = Some(0);
|
||||
let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint.
|
||||
let y = x.map(|x| u32::clone(x));
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `copied` method
|
||||
let y = x.map(|x| Clone::clone(x));
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `copied` method
|
||||
|
||||
// Should not suggest `copied` or `cloned` here since `T` is not a reference.
|
||||
let x: Option<u32> = Some(0);
|
||||
let y = x.map(|x| u32::clone(&x));
|
||||
let y = x.map(|x| Clone::clone(&x));
|
||||
|
||||
// Testing with `Result` now.
|
||||
let x: Result<String, ()> = Ok(String::new());
|
||||
let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint.
|
||||
let y = x.map(|x| String::clone(x));
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
let y = x.map(|x| String::clone(x));
|
||||
//~| HELP: consider calling the dedicated `cloned` method
|
||||
let y = x.map(|x| Clone::clone(x));
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `cloned` method
|
||||
|
||||
let x: Result<u32, ()> = Ok(0);
|
||||
let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint.
|
||||
let y = x.map(|x| u32::clone(x));
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `copied` method
|
||||
let y = x.map(|x| Clone::clone(x));
|
||||
//~^ ERROR: you are explicitly cloning with `.map()`
|
||||
//~| HELP: consider calling the dedicated `copied` method
|
||||
|
||||
// Should not suggest `copied` or `cloned` here since `T` is not a reference.
|
||||
let x: Result<u32, ()> = Ok(0);
|
||||
let y = x.map(|x| u32::clone(&x));
|
||||
let y = x.map(|x| Clone::clone(&x));
|
||||
|
||||
// We ensure that no warning is emitted here because `useless_asref` is taking over.
|
||||
let x = Some(String::new());
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue