mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-22 12:43:18 +00:00
Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
224d1e323a
186 changed files with 4085 additions and 1775 deletions
2
.github/deploy.sh
vendored
2
.github/deploy.sh
vendored
|
@ -8,8 +8,8 @@ rm -rf out/master/ || exit 0
|
|||
echo "Making the docs for master"
|
||||
mkdir out/master/
|
||||
cp util/gh-pages/index.html out/master
|
||||
cp util/gh-pages/theme.js out/master
|
||||
cp util/gh-pages/script.js out/master
|
||||
cp util/gh-pages/lints.json out/master
|
||||
cp util/gh-pages/style.css out/master
|
||||
|
||||
if [[ -n $TAG_NAME ]]; then
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -34,6 +34,7 @@ out
|
|||
|
||||
# gh pages docs
|
||||
util/gh-pages/lints.json
|
||||
util/gh-pages/index.html
|
||||
|
||||
# rustfmt backups
|
||||
*.rs.bk
|
||||
|
|
42
CHANGELOG.md
42
CHANGELOG.md
|
@ -6,11 +6,46 @@ document.
|
|||
|
||||
## Unreleased / Beta / In Rust Nightly
|
||||
|
||||
[b794b8e0...master](https://github.com/rust-lang/rust-clippy/compare/b794b8e0...master)
|
||||
[0f8eabd6...master](https://github.com/rust-lang/rust-clippy/compare/0f8eabd6...master)
|
||||
|
||||
## Rust 1.82
|
||||
|
||||
Current stable, released 2024-10-17
|
||||
|
||||
[View all 108 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-07-11T20%3A12%3A07Z..2024-08-24T20%3A55%3A35Z+base%3Amaster)
|
||||
|
||||
### New Lints
|
||||
|
||||
* Added [`too_long_first_doc_paragraph`] to `nursery`
|
||||
[#12993](https://github.com/rust-lang/rust-clippy/pull/12993)
|
||||
* Added [`unused_result_ok`] to `restriction`
|
||||
[#12150](https://github.com/rust-lang/rust-clippy/pull/12150)
|
||||
* Added [`pathbuf_init_then_push`] to `restriction`
|
||||
[#11700](https://github.com/rust-lang/rust-clippy/pull/11700)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [`explicit_iter_loop`]: Now respects the `msrv` configuration
|
||||
[#13288](https://github.com/rust-lang/rust-clippy/pull/13288)
|
||||
* [`assigning_clones`]: No longer lints in test code
|
||||
[#13273](https://github.com/rust-lang/rust-clippy/pull/13273)
|
||||
* [`inconsistent_struct_constructor`]: Lint attributes now work on the struct definition
|
||||
[#13211](https://github.com/rust-lang/rust-clippy/pull/13211)
|
||||
* [`set_contains_or_insert`]: Now also checks for `BTreeSet`
|
||||
[#13053](https://github.com/rust-lang/rust-clippy/pull/13053)
|
||||
* [`doc_markdown`]: Added the following identifiers to [`doc-valid-idents`]: AccessKit,
|
||||
CoreFoundation, CoreGraphics, CoreText, Direct2D, Direct3D, DirectWrite, PostScript,
|
||||
OpenAL, OpenType, WebRTC, WebSocket, WebTransport, NetBSD, and OpenBSD
|
||||
[#13093](https://github.com/rust-lang/rust-clippy/pull/13093)
|
||||
|
||||
### ICE Fixes
|
||||
|
||||
* [`uninit_vec`]
|
||||
[rust#128720](https://github.com/rust-lang/rust/pull/128720)
|
||||
|
||||
## Rust 1.81
|
||||
|
||||
Current stable, released 2024-09-05
|
||||
Released 2024-09-05
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -5621,6 +5656,7 @@ Released 2018-09-13
|
|||
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
|
||||
[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
|
||||
[`manual_hash_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one
|
||||
[`manual_ignore_case_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ignore_case_cmp
|
||||
[`manual_inspect`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_inspect
|
||||
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
|
||||
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
|
||||
|
@ -5874,6 +5910,7 @@ Released 2018-09-13
|
|||
[`ref_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option
|
||||
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
|
||||
[`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns
|
||||
[`regex_creation_in_loops`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_creation_in_loops
|
||||
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
|
||||
[`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params
|
||||
[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
|
||||
|
@ -6027,6 +6064,7 @@ Released 2018-09-13
|
|||
[`unnecessary_get_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_get_then_check
|
||||
[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
|
||||
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
|
||||
[`unnecessary_literal_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_bound
|
||||
[`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
|
||||
[`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor
|
||||
[`unnecessary_min_or_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min_or_max
|
||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -23,7 +23,7 @@ path = "src/driver.rs"
|
|||
[dependencies]
|
||||
clippy_config = { path = "clippy_config" }
|
||||
clippy_lints = { path = "clippy_lints" }
|
||||
rustc_tools_util = "0.3.0"
|
||||
rustc_tools_util = "0.4.0"
|
||||
tempfile = { version = "3.3", optional = true }
|
||||
termize = "0.1"
|
||||
color-print = "0.3.4"
|
||||
|
@ -39,6 +39,8 @@ toml = "0.7.3"
|
|||
walkdir = "2.3"
|
||||
filetime = "0.2.9"
|
||||
itertools = "0.12"
|
||||
pulldown-cmark = "0.11"
|
||||
rinja = { version = "0.3", default-features = false, features = ["config"] }
|
||||
|
||||
# UI test dependencies
|
||||
clippy_utils = { path = "clippy_utils" }
|
||||
|
@ -50,7 +52,7 @@ parking_lot = "0.12"
|
|||
tokio = { version = "1", features = ["io-util"] }
|
||||
|
||||
[build-dependencies]
|
||||
rustc_tools_util = "0.3.0"
|
||||
rustc_tools_util = "0.4.0"
|
||||
|
||||
[features]
|
||||
integration = ["tempfile"]
|
||||
|
@ -67,3 +69,10 @@ harness = false
|
|||
[[test]]
|
||||
name = "dogfood"
|
||||
harness = false
|
||||
|
||||
# quine-mc_cluskey makes up a significant part of the runtime in dogfood
|
||||
# due to the number of conditions in the clippy_lints crate
|
||||
# and enabling optimizations for that specific dependency helps a bit
|
||||
# without increasing total build times.
|
||||
[profile.dev.package.quine-mc_cluskey]
|
||||
opt-level = 3
|
||||
|
|
|
@ -329,7 +329,7 @@ arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
|
|||
## `array-size-threshold`
|
||||
The maximum allowed size for arrays on the stack
|
||||
|
||||
**Default Value:** `512000`
|
||||
**Default Value:** `16384`
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
|
|
|
@ -97,6 +97,30 @@ impl ConfError {
|
|||
}
|
||||
}
|
||||
|
||||
// Remove code tags and code behind '# 's, as they are not needed for the lint docs and --explain
|
||||
pub fn sanitize_explanation(raw_docs: &str) -> String {
|
||||
// Remove tags and hidden code:
|
||||
let mut explanation = String::with_capacity(128);
|
||||
let mut in_code = false;
|
||||
for line in raw_docs.lines().map(str::trim) {
|
||||
if let Some(lang) = line.strip_prefix("```") {
|
||||
let tag = lang.split_once(',').map_or(lang, |(left, _)| left);
|
||||
if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") {
|
||||
explanation += "```rust\n";
|
||||
} else {
|
||||
explanation += line;
|
||||
explanation.push('\n');
|
||||
}
|
||||
in_code = !in_code;
|
||||
} else if !(in_code && line.starts_with("# ")) {
|
||||
explanation += line;
|
||||
explanation.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
explanation
|
||||
}
|
||||
|
||||
macro_rules! wrap_option {
|
||||
() => {
|
||||
None
|
||||
|
@ -366,7 +390,7 @@ define_Conf! {
|
|||
arithmetic_side_effects_allowed_unary: Vec<String> = <_>::default(),
|
||||
/// The maximum allowed size for arrays on the stack
|
||||
#[lints(large_const_arrays, large_stack_arrays)]
|
||||
array_size_threshold: u64 = 512_000,
|
||||
array_size_threshold: u64 = 16 * 1024,
|
||||
/// Suppress lints whenever the suggested change would cause breakage for other crates.
|
||||
#[lints(
|
||||
box_collection,
|
||||
|
|
|
@ -26,5 +26,5 @@ mod metadata;
|
|||
pub mod msrvs;
|
||||
pub mod types;
|
||||
|
||||
pub use conf::{Conf, get_configuration_metadata, lookup_conf_file};
|
||||
pub use conf::{Conf, get_configuration_metadata, lookup_conf_file, sanitize_explanation};
|
||||
pub use metadata::ClippyConfiguration;
|
||||
|
|
|
@ -17,8 +17,7 @@ macro_rules! msrv_aliases {
|
|||
|
||||
// names may refer to stabilized feature flags or library items
|
||||
msrv_aliases! {
|
||||
1,83,0 { CONST_EXTERN_FN }
|
||||
1,83,0 { CONST_FLOAT_BITS_CONV }
|
||||
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY }
|
||||
1,82,0 { IS_NONE_OR }
|
||||
1,81,0 { LINT_REASONS_STABILIZATION }
|
||||
1,80,0 { BOX_INTO_ITER}
|
||||
|
|
|
@ -9,7 +9,7 @@ aho-corasick = "1.0"
|
|||
clap = { version = "4.4", features = ["derive"] }
|
||||
indoc = "1.0"
|
||||
itertools = "0.12"
|
||||
opener = "0.6"
|
||||
opener = "0.7"
|
||||
shell-escape = "0.1"
|
||||
walkdir = "2.3"
|
||||
|
||||
|
|
|
@ -207,13 +207,13 @@ pub(crate) fn get_stabilization_version() -> String {
|
|||
|
||||
fn get_test_file_contents(lint_name: &str, msrv: bool) -> String {
|
||||
let mut test = formatdoc!(
|
||||
r#"
|
||||
r"
|
||||
#![warn(clippy::{lint_name})]
|
||||
|
||||
fn main() {{
|
||||
// test code goes here
|
||||
}}
|
||||
"#
|
||||
"
|
||||
);
|
||||
|
||||
if msrv {
|
||||
|
@ -272,23 +272,23 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
|||
|
||||
result.push_str(&if enable_msrv {
|
||||
formatdoc!(
|
||||
r#"
|
||||
r"
|
||||
use clippy_config::msrvs::{{self, Msrv}};
|
||||
use clippy_config::Conf;
|
||||
{pass_import}
|
||||
use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
"#
|
||||
"
|
||||
)
|
||||
} else {
|
||||
formatdoc!(
|
||||
r#"
|
||||
r"
|
||||
{pass_import}
|
||||
use rustc_lint::{{{context_import}, {pass_type}}};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
"#
|
||||
"
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -296,7 +296,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
|||
|
||||
result.push_str(&if enable_msrv {
|
||||
formatdoc!(
|
||||
r#"
|
||||
r"
|
||||
pub struct {name_camel} {{
|
||||
msrv: Msrv,
|
||||
}}
|
||||
|
@ -315,15 +315,15 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
|||
|
||||
// TODO: Add MSRV level to `clippy_config/src/msrvs.rs` if needed.
|
||||
// TODO: Update msrv config comment in `clippy_config/src/conf.rs`
|
||||
"#
|
||||
"
|
||||
)
|
||||
} else {
|
||||
formatdoc!(
|
||||
r#"
|
||||
r"
|
||||
declare_lint_pass!({name_camel} => [{name_upper}]);
|
||||
|
||||
impl {pass_type}{pass_lifetimes} for {name_camel} {{}}
|
||||
"#
|
||||
"
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -416,7 +416,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
|
|||
} else {
|
||||
let _: fmt::Result = writedoc!(
|
||||
lint_file_contents,
|
||||
r#"
|
||||
r"
|
||||
use rustc_lint::{{{context_import}, LintContext}};
|
||||
|
||||
use super::{name_upper};
|
||||
|
@ -425,7 +425,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
|
|||
pub(super) fn check(cx: &{context_import}{pass_lifetimes}) {{
|
||||
todo!();
|
||||
}}
|
||||
"#
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -470,7 +470,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
|
|||
});
|
||||
|
||||
// Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl
|
||||
while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token == TokenKind::Ident) {
|
||||
while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) {
|
||||
let mut iter = iter
|
||||
.by_ref()
|
||||
.filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
|
||||
|
@ -480,7 +480,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
|
|||
// matches `!{`
|
||||
match_tokens!(iter, Bang OpenBrace);
|
||||
if let Some(LintDeclSearchResult { range, .. }) =
|
||||
iter.find(|result| result.token == TokenKind::CloseBrace)
|
||||
iter.find(|result| result.token_kind == TokenKind::CloseBrace)
|
||||
{
|
||||
last_decl_curly_offset = Some(range.end);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,9 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
|
|||
});
|
||||
|
||||
loop {
|
||||
if mtime("util/gh-pages/lints.json") < mtime("clippy_lints/src") {
|
||||
let index_time = mtime("util/gh-pages/index.html");
|
||||
|
||||
if index_time < mtime("clippy_lints/src") || index_time < mtime("util/gh-pages/index_template.html") {
|
||||
Command::new(env::var("CARGO").unwrap_or("cargo".into()))
|
||||
.arg("collect-metadata")
|
||||
.spawn()
|
||||
|
|
|
@ -13,7 +13,6 @@ arrayvec = { version = "0.7", default-features = false }
|
|||
cargo_metadata = "0.18"
|
||||
clippy_config = { path = "../clippy_config" }
|
||||
clippy_utils = { path = "../clippy_utils" }
|
||||
declare_clippy_lint = { path = "../declare_clippy_lint" }
|
||||
itertools = "0.12"
|
||||
quine-mc_cluskey = "0.2"
|
||||
regex-syntax = "0.8"
|
||||
|
|
|
@ -47,7 +47,7 @@ impl LateLintPass<'_> for BoxDefault {
|
|||
// And the call is that of a `Box` method
|
||||
&& path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box())
|
||||
// And the single argument to the call is another function call
|
||||
// This is the `T::default()` of `Box::new(T::default())`
|
||||
// This is the `T::default()` (or default equivalent) of `Box::new(T::default())`
|
||||
&& let ExprKind::Call(arg_path, _) = arg.kind
|
||||
// And we are not in a foreign crate's macro
|
||||
&& !in_external_macro(cx.sess(), expr.span)
|
||||
|
|
|
@ -41,7 +41,7 @@ impl EarlyLintPass for ByteCharSlice {
|
|||
"can be more succinctly written as a byte str",
|
||||
"try",
|
||||
format!("b\"{slice}\""),
|
||||
Applicability::MaybeIncorrect,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ pub(super) fn check(
|
|||
if msrv.meets(msrvs::UNSIGNED_ABS)
|
||||
&& let ty::Int(from) = cast_from.kind()
|
||||
&& let ty::Uint(to) = cast_to.kind()
|
||||
&& let ExprKind::MethodCall(method_path, receiver, ..) = cast_expr.kind
|
||||
&& let ExprKind::MethodCall(method_path, receiver, [], _) = cast_expr.kind
|
||||
&& method_path.ident.name.as_str() == "abs"
|
||||
{
|
||||
let span = if from.bit_width() == to.bit_width() {
|
||||
|
|
|
@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
cx.typeck_results().expr_ty(expr),
|
||||
);
|
||||
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
|
||||
} else if let ExprKind::MethodCall(method_path, self_arg, ..) = &expr.kind {
|
||||
} else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind {
|
||||
if method_path.ident.name == sym!(cast)
|
||||
&& let Some(generic_args) = method_path.args
|
||||
&& let [GenericArg::Type(cast_to)] = generic_args.args
|
||||
|
|
|
@ -34,7 +34,7 @@ declare_lint_pass!(CreateDir => [CREATE_DIR]);
|
|||
|
||||
impl LateLintPass<'_> for CreateDir {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Call(func, [arg, ..]) = expr.kind
|
||||
if let ExprKind::Call(func, [arg]) = expr.kind
|
||||
&& let ExprKind::Path(ref path) = func.kind
|
||||
&& let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id)
|
||||
|
|
162
clippy_lints/src/declare_clippy_lint.rs
Normal file
162
clippy_lints/src/declare_clippy_lint.rs
Normal file
|
@ -0,0 +1,162 @@
|
|||
#[macro_export]
|
||||
#[allow(clippy::crate_in_macro_def)]
|
||||
macro_rules! declare_clippy_lint {
|
||||
(@
|
||||
$(#[doc = $lit:literal])*
|
||||
pub $lint_name:ident,
|
||||
$category:ident,
|
||||
$lintcategory:expr,
|
||||
$desc:literal,
|
||||
$version_expr:expr,
|
||||
$version_lit:literal
|
||||
) => {
|
||||
rustc_session::declare_tool_lint! {
|
||||
$(#[doc = $lit])*
|
||||
#[clippy::version = $version_lit]
|
||||
pub clippy::$lint_name,
|
||||
$category,
|
||||
$desc,
|
||||
report_in_external_macro:true
|
||||
}
|
||||
|
||||
pub(crate) static ${concat($lint_name, _INFO)}: &'static crate::LintInfo = &crate::LintInfo {
|
||||
lint: &$lint_name,
|
||||
category: $lintcategory,
|
||||
explanation: concat!($($lit,"\n",)*),
|
||||
location: concat!(file!(), "#L", line!()),
|
||||
version: $version_expr
|
||||
};
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
restriction,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Allow, crate::LintCategory::Restriction, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
style,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Warn, crate::LintCategory::Style, $desc,
|
||||
Some($version), $version
|
||||
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
correctness,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Deny, crate::LintCategory::Correctness, $desc,
|
||||
Some($version), $version
|
||||
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
perf,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Warn, crate::LintCategory::Perf, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
complexity,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Warn, crate::LintCategory::Complexity, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
suspicious,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Warn, crate::LintCategory::Suspicious, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
nursery,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Allow, crate::LintCategory::Nursery, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
pedantic,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Allow, crate::LintCategory::Pedantic, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
cargo,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Allow, crate::LintCategory::Cargo, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
pub $lint_name:ident,
|
||||
internal,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Allow, crate::LintCategory::Internal, $desc,
|
||||
None, "0.0.0"
|
||||
}
|
||||
};
|
||||
}
|
|
@ -306,6 +306,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::manual_float_methods::MANUAL_IS_FINITE_INFO,
|
||||
crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
|
||||
crate::manual_hash_one::MANUAL_HASH_ONE_INFO,
|
||||
crate::manual_ignore_case_cmp::MANUAL_IGNORE_CASE_CMP_INFO,
|
||||
crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
|
||||
crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO,
|
||||
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
|
||||
|
@ -639,6 +640,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::ref_patterns::REF_PATTERNS_INFO,
|
||||
crate::reference::DEREF_ADDROF_INFO,
|
||||
crate::regex::INVALID_REGEX_INFO,
|
||||
crate::regex::REGEX_CREATION_IN_LOOPS_INFO,
|
||||
crate::regex::TRIVIAL_REGEX_INFO,
|
||||
crate::repeat_vec_with_capacity::REPEAT_VEC_WITH_CAPACITY_INFO,
|
||||
crate::reserve_after_initialization::RESERVE_AFTER_INITIALIZATION_INFO,
|
||||
|
@ -736,6 +738,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::unit_types::UNIT_CMP_INFO,
|
||||
crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO,
|
||||
crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO,
|
||||
crate::unnecessary_literal_bound::UNNECESSARY_LITERAL_BOUND_INFO,
|
||||
crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO,
|
||||
crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
|
||||
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
|
||||
|
|
|
@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
if !expr.span.from_expansion()
|
||||
// Avoid cases already linted by `field_reassign_with_default`
|
||||
&& !self.reassigned_linted.contains(&expr.span)
|
||||
&& let ExprKind::Call(path, ..) = expr.kind
|
||||
&& let ExprKind::Call(path, []) = expr.kind
|
||||
&& !in_automatically_derived(cx.tcx, expr.hir_id)
|
||||
&& let ExprKind::Path(ref qpath) = path.kind
|
||||
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
|
||||
|
@ -253,7 +253,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
|
||||
/// Checks if the given expression is the `default` method belonging to the `Default` trait.
|
||||
fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool {
|
||||
if let ExprKind::Call(fn_expr, _) = &expr.kind
|
||||
if let ExprKind::Call(fn_expr, []) = &expr.kind
|
||||
&& let ExprKind::Path(qpath) = &fn_expr.kind
|
||||
&& let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
|
||||
{
|
||||
|
|
|
@ -452,7 +452,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.82.0"]
|
||||
pub TOO_LONG_FIRST_DOC_PARAGRAPH,
|
||||
style,
|
||||
nursery,
|
||||
"ensure that the first line of a documentation paragraph isn't too long"
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ declare_lint_pass!(Exit => [EXIT]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for Exit {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Call(path_expr, _args) = e.kind
|
||||
if let ExprKind::Call(path_expr, [_]) = e.kind
|
||||
&& let ExprKind::Path(ref path) = path_expr.kind
|
||||
&& let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::process_exit, def_id)
|
||||
|
|
|
@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
|||
&& unwrap_fun.ident.name == sym::unwrap
|
||||
// match call to write_fmt
|
||||
&& let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = *look_in_block(cx, &write_call.kind)
|
||||
&& let ExprKind::Call(write_recv_path, _) = write_recv.kind
|
||||
&& let ExprKind::Call(write_recv_path, []) = write_recv.kind
|
||||
&& write_fun.ident.name == sym!(write_fmt)
|
||||
&& let Some(def_id) = path_def_id(cx, write_recv_path)
|
||||
{
|
||||
|
|
|
@ -436,12 +436,12 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
lhs,
|
||||
rhs,
|
||||
) = expr.kind
|
||||
&& let ExprKind::MethodCall(path, self_arg, [], _) = &lhs.kind
|
||||
&& path.ident.name.as_str() == "exp"
|
||||
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
|
||||
&& let Some(value) = ConstEvalCtxt::new(cx).eval(rhs)
|
||||
&& (F32(1.0) == value || F64(1.0) == value)
|
||||
&& let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind
|
||||
&& cx.typeck_results().expr_ty(self_arg).is_floating_point()
|
||||
&& path.ident.name.as_str() == "exp"
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
@ -151,7 +151,7 @@ struct FormatImplExpr<'a, 'tcx> {
|
|||
impl FormatImplExpr<'_, '_> {
|
||||
fn check_to_string_in_display(&self) {
|
||||
if self.format_trait_impl.name == sym::Display
|
||||
&& let ExprKind::MethodCall(path, self_arg, ..) = self.expr.kind
|
||||
&& let ExprKind::MethodCall(path, self_arg, [], _) = self.expr.kind
|
||||
// Get the hir_id of the object we are calling the method on
|
||||
// Is the method to_string() ?
|
||||
&& path.ident.name == sym::to_string
|
||||
|
|
|
@ -181,6 +181,9 @@ fn convert_to_from(
|
|||
let from = self_ty.span.get_source_text(cx)?;
|
||||
let into = target_ty.span.get_source_text(cx)?;
|
||||
|
||||
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
|
||||
// ~~~~ ~~~~
|
||||
|
@ -197,13 +200,10 @@ fn convert_to_from(
|
|||
// fn into([mut] self) -> T -> fn into([mut] v: T) -> T
|
||||
// ~~~~ ~~~~
|
||||
(self_ident.span, format!("val: {from}")),
|
||||
];
|
||||
|
||||
if let FnRetTy::Return(_) = sig.decl.output {
|
||||
// fn into(self) -> T -> fn into(self) -> Self
|
||||
// ~ ~~~~
|
||||
suggestions.push((sig.decl.output.span(), String::from("Self")));
|
||||
}
|
||||
(sig.decl.output.span(), return_type),
|
||||
];
|
||||
|
||||
let mut finder = SelfFinder {
|
||||
cx,
|
||||
|
|
|
@ -82,7 +82,7 @@ fn mutex_lock_call<'tcx>(
|
|||
expr: &'tcx Expr<'_>,
|
||||
op_mutex: Option<&'tcx Expr<'_>>,
|
||||
) -> ControlFlow<&'tcx Expr<'tcx>> {
|
||||
if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
|
||||
if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind
|
||||
&& path.ident.as_str() == "lock"
|
||||
&& let ty = cx.typeck_results().expr_ty(self_arg).peel_refs()
|
||||
&& is_type_diagnostic_item(cx, ty, sym::Mutex)
|
||||
|
|
|
@ -139,6 +139,13 @@ fn check_manual_check<'tcx>(
|
|||
if_block,
|
||||
else_block,
|
||||
msrv,
|
||||
matches!(
|
||||
clippy_utils::get_parent_expr(cx, expr),
|
||||
Some(Expr {
|
||||
kind: ExprKind::If(..),
|
||||
..
|
||||
})
|
||||
),
|
||||
),
|
||||
BinOpKind::Lt | BinOpKind::Le => check_gt(
|
||||
cx,
|
||||
|
@ -149,6 +156,13 @@ fn check_manual_check<'tcx>(
|
|||
if_block,
|
||||
else_block,
|
||||
msrv,
|
||||
matches!(
|
||||
clippy_utils::get_parent_expr(cx, expr),
|
||||
Some(Expr {
|
||||
kind: ExprKind::If(..),
|
||||
..
|
||||
})
|
||||
),
|
||||
),
|
||||
_ => {},
|
||||
}
|
||||
|
@ -165,6 +179,7 @@ fn check_gt(
|
|||
if_block: &Expr<'_>,
|
||||
else_block: &Expr<'_>,
|
||||
msrv: &Msrv,
|
||||
is_composited: bool,
|
||||
) {
|
||||
if let Some(big_var) = Var::new(big_var)
|
||||
&& let Some(little_var) = Var::new(little_var)
|
||||
|
@ -178,6 +193,7 @@ fn check_gt(
|
|||
if_block,
|
||||
else_block,
|
||||
msrv,
|
||||
is_composited,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -206,6 +222,7 @@ fn check_subtraction(
|
|||
if_block: &Expr<'_>,
|
||||
else_block: &Expr<'_>,
|
||||
msrv: &Msrv,
|
||||
is_composited: bool,
|
||||
) {
|
||||
let if_block = peel_blocks(if_block);
|
||||
let else_block = peel_blocks(else_block);
|
||||
|
@ -226,6 +243,7 @@ fn check_subtraction(
|
|||
else_block,
|
||||
if_block,
|
||||
msrv,
|
||||
is_composited,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -242,13 +260,18 @@ fn check_subtraction(
|
|||
&& let Some(little_var_snippet) = snippet_opt(cx, little_var.span)
|
||||
&& (!is_in_const_context(cx) || msrv.meets(msrvs::SATURATING_SUB_CONST))
|
||||
{
|
||||
let sugg = format!(
|
||||
"{}{big_var_snippet}.saturating_sub({little_var_snippet}){}",
|
||||
if is_composited { "{ " } else { "" },
|
||||
if is_composited { " }" } else { "" }
|
||||
);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
IMPLICIT_SATURATING_SUB,
|
||||
expr_span,
|
||||
"manual arithmetic check found",
|
||||
"replace it with",
|
||||
format!("{big_var_snippet}.saturating_sub({little_var_snippet})"),
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ use clippy_utils::source::snippet;
|
|||
use rustc_errors::{Applicability, SuggestionStyle};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{
|
||||
AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifier,
|
||||
TyKind, WherePredicate,
|
||||
AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifier, TyKind,
|
||||
WherePredicate,
|
||||
};
|
||||
use rustc_hir_analysis::lower_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
|
@ -50,11 +50,28 @@ declare_clippy_lint! {
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects type names that are prefixed or suffixed by the
|
||||
/// containing module's name.
|
||||
/// Detects public item names that are prefixed or suffixed by the
|
||||
/// containing public module's name.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It requires the user to type the module name twice.
|
||||
/// It requires the user to type the module name twice in each usage,
|
||||
/// especially if they choose to import the module rather than its contents.
|
||||
///
|
||||
/// Lack of such repetition is also the style used in the Rust standard library;
|
||||
/// e.g. `io::Error` and `fmt::Error` rather than `io::IoError` and `fmt::FmtError`;
|
||||
/// and `array::from_ref` rather than `array::array_from_ref`.
|
||||
///
|
||||
/// ### Known issues
|
||||
/// Glob re-exports are ignored; e.g. this will not warn even though it should:
|
||||
///
|
||||
/// ```no_run
|
||||
/// pub mod foo {
|
||||
/// mod iteration {
|
||||
/// pub struct FooIter {}
|
||||
/// }
|
||||
/// pub use iteration::*; // creates the path `foo::FooIter`
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
|
@ -71,7 +88,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.33.0"]
|
||||
pub MODULE_NAME_REPETITIONS,
|
||||
pedantic,
|
||||
restriction,
|
||||
"type names prefixed/postfixed with their containing module's name"
|
||||
}
|
||||
|
||||
|
@ -389,12 +406,12 @@ impl LateLintPass<'_> for ItemNameRepetitions {
|
|||
let item_name = item.ident.name.as_str();
|
||||
let item_camel = to_camel_case(item_name);
|
||||
if !item.span.from_expansion() && is_present_in_source(cx, item.span) {
|
||||
if let [.., (mod_name, mod_camel, owner_id)] = &*self.modules {
|
||||
if let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules {
|
||||
// constants don't have surrounding modules
|
||||
if !mod_camel.is_empty() {
|
||||
if mod_name == &item.ident.name
|
||||
&& let ItemKind::Mod(..) = item.kind
|
||||
&& (!self.allow_private_module_inception || cx.tcx.visibility(owner_id.def_id).is_public())
|
||||
&& (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public())
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
|
@ -403,9 +420,13 @@ impl LateLintPass<'_> for ItemNameRepetitions {
|
|||
"module has the same name as its containing module",
|
||||
);
|
||||
}
|
||||
|
||||
// The `module_name_repetitions` lint should only trigger if the item has the module in its
|
||||
// name. Having the same name is accepted.
|
||||
if cx.tcx.visibility(item.owner_id).is_public() && item_camel.len() > mod_camel.len() {
|
||||
if cx.tcx.visibility(item.owner_id).is_public()
|
||||
&& cx.tcx.visibility(mod_owner_id.def_id).is_public()
|
||||
&& item_camel.len() > mod_camel.len()
|
||||
{
|
||||
let matching = count_match_start(mod_camel, &item_camel);
|
||||
let rmatching = count_match_end(mod_camel, &item_camel);
|
||||
let nchars = mod_camel.chars().count();
|
||||
|
|
|
@ -57,7 +57,7 @@ impl_lint_pass!(LargeFuture => [LARGE_FUTURES]);
|
|||
impl<'tcx> LateLintPass<'tcx> for LargeFuture {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let ExprKind::Match(scrutinee, _, MatchSource::AwaitDesugar) = expr.kind
|
||||
&& let ExprKind::Call(func, [arg, ..]) = scrutinee.kind
|
||||
&& let ExprKind::Call(func, [arg]) = scrutinee.kind
|
||||
&& let ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..)) = func.kind
|
||||
&& !expr.span.from_expansion()
|
||||
&& let ty = cx.typeck_results().expr_ty(arg)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::num::Saturating;
|
||||
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
|
@ -30,6 +32,7 @@ declare_clippy_lint! {
|
|||
pub struct LargeStackArrays {
|
||||
maximum_allowed_size: u64,
|
||||
prev_vec_macro_callsite: Option<Span>,
|
||||
const_item_counter: Saturating<u16>,
|
||||
}
|
||||
|
||||
impl LargeStackArrays {
|
||||
|
@ -37,6 +40,7 @@ impl LargeStackArrays {
|
|||
Self {
|
||||
maximum_allowed_size: conf.array_size_threshold,
|
||||
prev_vec_macro_callsite: None,
|
||||
const_item_counter: Saturating(0),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,8 +64,21 @@ impl LargeStackArrays {
|
|||
impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
|
||||
fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if matches!(item.kind, ItemKind::Static(..) | ItemKind::Const(..)) {
|
||||
self.const_item_counter += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item_post(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if matches!(item.kind, ItemKind::Static(..) | ItemKind::Const(..)) {
|
||||
self.const_item_counter -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
|
||||
if self.const_item_counter.0 == 0
|
||||
&& let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
|
||||
&& !self.is_from_vec_macro(cx, expr.span)
|
||||
&& let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
|
||||
&& let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind()
|
||||
|
|
|
@ -513,7 +513,7 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>
|
|||
return;
|
||||
}
|
||||
|
||||
if let (&ExprKind::MethodCall(method_path, receiver, args, _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) {
|
||||
if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) {
|
||||
// check if we are in an is_empty() method
|
||||
if let Some(name) = get_item_name(cx, method) {
|
||||
if name.as_str() == "is_empty" {
|
||||
|
@ -521,29 +521,17 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>
|
|||
}
|
||||
}
|
||||
|
||||
check_len(
|
||||
cx,
|
||||
span,
|
||||
method_path.ident.name,
|
||||
receiver,
|
||||
args,
|
||||
&lit.node,
|
||||
op,
|
||||
compare_to,
|
||||
);
|
||||
check_len(cx, span, method_path.ident.name, receiver, &lit.node, op, compare_to);
|
||||
} else {
|
||||
check_empty_expr(cx, span, method, lit, op);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(flip1995): Figure out how to reduce the number of arguments
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn check_len(
|
||||
cx: &LateContext<'_>,
|
||||
span: Span,
|
||||
method_name: Symbol,
|
||||
receiver: &Expr<'_>,
|
||||
args: &[Expr<'_>],
|
||||
lit: &LitKind,
|
||||
op: &str,
|
||||
compare_to: u32,
|
||||
|
@ -554,7 +542,7 @@ fn check_len(
|
|||
return;
|
||||
}
|
||||
|
||||
if method_name == sym::len && args.is_empty() && has_is_empty(cx, receiver) {
|
||||
if method_name == sym::len && has_is_empty(cx, receiver) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#![feature(array_windows)]
|
||||
#![feature(binary_heap_into_iter_sorted)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![feature(f128)]
|
||||
#![feature(f16)]
|
||||
#![feature(if_let_guard)]
|
||||
|
@ -56,9 +57,10 @@ extern crate rustc_trait_selection;
|
|||
extern crate thin_vec;
|
||||
|
||||
#[macro_use]
|
||||
extern crate clippy_utils;
|
||||
mod declare_clippy_lint;
|
||||
|
||||
#[macro_use]
|
||||
extern crate declare_clippy_lint;
|
||||
extern crate clippy_utils;
|
||||
|
||||
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
|
||||
mod utils;
|
||||
|
@ -203,6 +205,7 @@ mod manual_clamp;
|
|||
mod manual_div_ceil;
|
||||
mod manual_float_methods;
|
||||
mod manual_hash_one;
|
||||
mod manual_ignore_case_cmp;
|
||||
mod manual_is_ascii_check;
|
||||
mod manual_is_power_of_two;
|
||||
mod manual_let_else;
|
||||
|
@ -360,6 +363,7 @@ mod unit_return_expecting_ord;
|
|||
mod unit_types;
|
||||
mod unnamed_address;
|
||||
mod unnecessary_box_returns;
|
||||
mod unnecessary_literal_bound;
|
||||
mod unnecessary_map_on_constructor;
|
||||
mod unnecessary_owned_empty_strings;
|
||||
mod unnecessary_self_imports;
|
||||
|
@ -391,7 +395,7 @@ mod zero_sized_map_values;
|
|||
mod zombie_processes;
|
||||
// end lints modules, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
use clippy_config::{Conf, get_configuration_metadata};
|
||||
use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
|
||||
use clippy_utils::macros::FormatArgsStorage;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_lint::{Lint, LintId};
|
||||
|
@ -519,8 +523,9 @@ impl LintInfo {
|
|||
|
||||
pub fn explain(name: &str) -> i32 {
|
||||
let target = format!("clippy::{}", name.to_ascii_uppercase());
|
||||
|
||||
if let Some(info) = declared_lints::LINTS.iter().find(|info| info.lint.name == target) {
|
||||
println!("{}", info.explanation);
|
||||
println!("{}", sanitize_explanation(info.explanation));
|
||||
// Check if the lint has configuration
|
||||
let mut mdconf = get_configuration_metadata();
|
||||
let name = name.to_ascii_lowercase();
|
||||
|
@ -896,7 +901,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns));
|
||||
store.register_early_pass(|| Box::new(visibility::Visibility));
|
||||
store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods));
|
||||
store.register_late_pass(move |_| Box::new(manual_float_methods::ManualFloatMethods::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes));
|
||||
store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError));
|
||||
store.register_late_pass(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf)));
|
||||
|
@ -941,5 +946,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo));
|
||||
store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions));
|
||||
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
|
||||
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::FnRetTy::Return;
|
||||
use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
|
||||
use rustc_hir::intravisit::{
|
||||
Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound,
|
||||
walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_where_predicate,
|
||||
Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound, walk_poly_trait_ref,
|
||||
walk_trait_ref, walk_ty, walk_where_predicate,
|
||||
};
|
||||
use rustc_hir::{
|
||||
BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics,
|
||||
|
|
|
@ -412,7 +412,6 @@ impl LiteralDigitGrouping {
|
|||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::module_name_repetitions)]
|
||||
pub struct DecimalLiteralRepresentation {
|
||||
threshold: u64,
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ pub(super) fn check<'tcx>(
|
|||
let mut loop_visitor = LoopVisitor {
|
||||
cx,
|
||||
label,
|
||||
inner_labels: label.into_iter().collect(),
|
||||
is_finite: false,
|
||||
loop_depth: 0,
|
||||
};
|
||||
|
@ -93,6 +94,7 @@ fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option
|
|||
struct LoopVisitor<'hir, 'tcx> {
|
||||
cx: &'hir LateContext<'tcx>,
|
||||
label: Option<Label>,
|
||||
inner_labels: Vec<Label>,
|
||||
loop_depth: usize,
|
||||
is_finite: bool,
|
||||
}
|
||||
|
@ -108,11 +110,24 @@ impl<'hir> Visitor<'hir> for LoopVisitor<'hir, '_> {
|
|||
self.is_finite = true;
|
||||
}
|
||||
},
|
||||
ExprKind::Continue(hir::Destination { label, .. }) => {
|
||||
// Check whether we are leaving this loop by continuing into an outer loop
|
||||
// whose label we did not encounter.
|
||||
if label.is_some_and(|label| !self.inner_labels.contains(&label)) {
|
||||
self.is_finite = true;
|
||||
}
|
||||
},
|
||||
ExprKind::Ret(..) => self.is_finite = true,
|
||||
ExprKind::Loop(..) => {
|
||||
ExprKind::Loop(_, label, _, _) => {
|
||||
if let Some(label) = label {
|
||||
self.inner_labels.push(*label);
|
||||
}
|
||||
self.loop_depth += 1;
|
||||
walk_expr(self, ex);
|
||||
self.loop_depth = self.loop_depth.saturating_sub(1);
|
||||
self.loop_depth -= 1;
|
||||
if label.is_some() {
|
||||
self.inner_labels.pop();
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// Calls to a function that never return
|
||||
|
|
|
@ -47,8 +47,9 @@ fn report_lint(cx: &LateContext<'_>, pop_span: Span, pop_stmt_kind: PopStmt<'_>,
|
|||
);
|
||||
}
|
||||
|
||||
fn match_method_call(cx: &LateContext<'_>, expr: &Expr<'_>, method: Symbol) -> bool {
|
||||
if let ExprKind::MethodCall(..) = expr.kind
|
||||
fn match_method_call<const ARGS_COUNT: usize>(cx: &LateContext<'_>, expr: &Expr<'_>, method: Symbol) -> bool {
|
||||
if let ExprKind::MethodCall(_, _, args, _) = expr.kind
|
||||
&& args.len() == ARGS_COUNT
|
||||
&& let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
{
|
||||
cx.tcx.is_diagnostic_item(method, id)
|
||||
|
@ -58,9 +59,9 @@ fn match_method_call(cx: &LateContext<'_>, expr: &Expr<'_>, method: Symbol) -> b
|
|||
}
|
||||
|
||||
fn is_vec_pop_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, is_empty_recv: &Expr<'_>) -> bool {
|
||||
if (match_method_call(cx, expr, sym::option_unwrap) || match_method_call(cx, expr, sym::option_expect))
|
||||
if (match_method_call::<0>(cx, expr, sym::option_unwrap) || match_method_call::<1>(cx, expr, sym::option_expect))
|
||||
&& let ExprKind::MethodCall(_, unwrap_recv, ..) = expr.kind
|
||||
&& match_method_call(cx, unwrap_recv, sym::vec_pop)
|
||||
&& match_method_call::<0>(cx, unwrap_recv, sym::vec_pop)
|
||||
&& let ExprKind::MethodCall(_, pop_recv, ..) = unwrap_recv.kind
|
||||
{
|
||||
// make sure they're the same `Vec`
|
||||
|
@ -96,7 +97,7 @@ fn check_call_arguments(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &E
|
|||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, full_cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>, loop_span: Span) {
|
||||
if let ExprKind::Unary(UnOp::Not, cond) = full_cond.kind
|
||||
&& let ExprKind::MethodCall(_, is_empty_recv, _, _) = cond.kind
|
||||
&& match_method_call(cx, cond, sym::vec_is_empty)
|
||||
&& match_method_call::<0>(cx, cond, sym::vec_is_empty)
|
||||
&& let ExprKind::Block(body, _) = body.kind
|
||||
&& let Some(stmt) = body.stmts.first()
|
||||
{
|
||||
|
|
|
@ -172,10 +172,8 @@ fn get_vec_push<'tcx>(
|
|||
stmt: &'tcx Stmt<'_>,
|
||||
) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)> {
|
||||
if let StmtKind::Semi(semi_stmt) = &stmt.kind
|
||||
// Extract method being called
|
||||
&& let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind
|
||||
// Figure out the parameters for the method call
|
||||
&& let Some(pushed_item) = args.first()
|
||||
// Extract method being called and figure out the parameters for the method call
|
||||
&& let ExprKind::MethodCall(path, self_expr, [pushed_item], _) = &semi_stmt.kind
|
||||
// Check that the method being called is push() on a Vec
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec)
|
||||
&& path.ident.name.as_str() == "push"
|
||||
|
|
|
@ -43,7 +43,6 @@ impl MacroRefData {
|
|||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[expect(clippy::module_name_repetitions)]
|
||||
pub struct MacroUseImports {
|
||||
/// the actual import path used and the span of the attribute above it. The value is
|
||||
/// the location, where the lint should be emitted.
|
||||
|
|
|
@ -42,7 +42,7 @@ impl LateLintPass<'_> for MainRecursion {
|
|||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Call(func, _) = &expr.kind
|
||||
if let ExprKind::Call(func, []) = &expr.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind
|
||||
&& let Some(def_id) = path.res.opt_def_id()
|
||||
&& is_entrypoint_fn(cx, def_id)
|
||||
|
|
|
@ -95,7 +95,7 @@ fn get_one_size_of_ty<'tcx>(
|
|||
}
|
||||
|
||||
fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> {
|
||||
if let ExprKind::Call(count_func, _func_args) = expr.kind
|
||||
if let ExprKind::Call(count_func, []) = expr.kind
|
||||
&& let ExprKind::Path(ref count_func_qpath) = count_func.kind
|
||||
&& let QPath::Resolved(_, count_func_path) = count_func_qpath
|
||||
&& let Some(segment_zero) = count_func_path.segments.first()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use clippy_config::msrvs::Msrv;
|
||||
use clippy_config::{Conf, msrvs};
|
||||
use clippy_utils::consts::{ConstEvalCtxt, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
|
@ -6,7 +8,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::{BinOpKind, Constness, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -56,7 +58,7 @@ declare_clippy_lint! {
|
|||
style,
|
||||
"use dedicated method to check if a float is finite"
|
||||
}
|
||||
declare_lint_pass!(ManualFloatMethods => [MANUAL_IS_INFINITE, MANUAL_IS_FINITE]);
|
||||
impl_lint_pass!(ManualFloatMethods => [MANUAL_IS_INFINITE, MANUAL_IS_FINITE]);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Variant {
|
||||
|
@ -80,6 +82,18 @@ impl Variant {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ManualFloatMethods {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl ManualFloatMethods {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
msrv: conf.msrv.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let ExprKind::Binary(kind, lhs, rhs) = expr.kind
|
||||
|
@ -92,7 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
|
|||
&& !in_external_macro(cx.sess(), expr.span)
|
||||
&& (
|
||||
matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst)
|
||||
|| cx.tcx.features().declared(sym!(const_float_classify))
|
||||
|| self.msrv.meets(msrvs::CONST_FLOAT_CLASSIFY)
|
||||
)
|
||||
&& let [first, second, const_1, const_2] = exprs
|
||||
&& let ecx = ConstEvalCtxt::new(cx)
|
||||
|
@ -150,6 +164,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn is_infinity(constant: &Constant<'_>) -> bool {
|
||||
|
|
127
clippy_lints/src/manual_ignore_case_cmp.rs
Normal file
127
clippy_lints/src/manual_ignore_case_cmp.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{get_type_diagnostic_name, is_type_diagnostic_item, is_type_lang_item};
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::ExprKind::{Binary, Lit, MethodCall};
|
||||
use rustc_hir::{BinOpKind, Expr, LangItem};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{Ty, UintTy};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for manual case-insensitive ASCII comparison.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The `eq_ignore_ascii_case` method is faster because it does not allocate
|
||||
/// memory for the new strings, and it is more readable.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn compare(a: &str, b: &str) -> bool {
|
||||
/// a.to_ascii_lowercase() == b.to_ascii_lowercase() || a.to_ascii_lowercase() == "abc"
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn compare(a: &str, b: &str) -> bool {
|
||||
/// a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc")
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.82.0"]
|
||||
pub MANUAL_IGNORE_CASE_CMP,
|
||||
perf,
|
||||
"manual case-insensitive ASCII comparison"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ManualIgnoreCaseCmp => [MANUAL_IGNORE_CASE_CMP]);
|
||||
|
||||
enum MatchType<'a, 'b> {
|
||||
ToAscii(bool, Ty<'a>),
|
||||
Literal(&'b LitKind),
|
||||
}
|
||||
|
||||
fn get_ascii_type<'a, 'b>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'b>) -> Option<(Span, MatchType<'a, 'b>)> {
|
||||
if let MethodCall(path, expr, _, _) = kind {
|
||||
let is_lower = match path.ident.name.as_str() {
|
||||
"to_ascii_lowercase" => true,
|
||||
"to_ascii_uppercase" => false,
|
||||
_ => return None,
|
||||
};
|
||||
let ty_raw = cx.typeck_results().expr_ty(expr);
|
||||
let ty = ty_raw.peel_refs();
|
||||
if needs_ref_to_cmp(cx, ty)
|
||||
|| ty.is_str()
|
||||
|| ty.is_slice()
|
||||
|| matches!(get_type_diagnostic_name(cx, ty), Some(sym::OsStr | sym::OsString))
|
||||
{
|
||||
return Some((expr.span, ToAscii(is_lower, ty_raw)));
|
||||
}
|
||||
} else if let Lit(expr) = kind {
|
||||
return Some((expr.span, Literal(&expr.node)));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns true if the type needs to be dereferenced to be compared
|
||||
fn needs_ref_to_cmp(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||
ty.is_char()
|
||||
|| *ty.kind() == ty::Uint(UintTy::U8)
|
||||
|| is_type_diagnostic_item(cx, ty, sym::Vec)
|
||||
|| is_type_lang_item(cx, ty, LangItem::String)
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for ManualIgnoreCaseCmp {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
|
||||
// check if expression represents a comparison of two strings
|
||||
// using .to_ascii_lowercase() or .to_ascii_uppercase() methods,
|
||||
// or one of the sides is a literal
|
||||
// Offer to replace it with .eq_ignore_ascii_case() method
|
||||
if let Binary(op, left, right) = &expr.kind
|
||||
&& (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne)
|
||||
&& let Some((left_span, left_val)) = get_ascii_type(cx, left.kind)
|
||||
&& let Some((right_span, right_val)) = get_ascii_type(cx, right.kind)
|
||||
&& match (&left_val, &right_val) {
|
||||
(ToAscii(l_lower, ..), ToAscii(r_lower, ..)) if l_lower == r_lower => true,
|
||||
(ToAscii(..), Literal(..)) | (Literal(..), ToAscii(..)) => true,
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
let deref = match right_val {
|
||||
ToAscii(_, ty) if needs_ref_to_cmp(cx, ty) => "&",
|
||||
ToAscii(..) => "",
|
||||
Literal(ty) => {
|
||||
if let LitKind::Char(_) | LitKind::Byte(_) = ty {
|
||||
"&"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
},
|
||||
};
|
||||
let neg = if op.node == BinOpKind::Ne { "!" } else { "" };
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MANUAL_IGNORE_CASE_CMP,
|
||||
expr.span,
|
||||
"manual case-insensitive ASCII comparison",
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
diag.span_suggestion_verbose(
|
||||
expr.span,
|
||||
"consider using `.eq_ignore_ascii_case()` instead",
|
||||
format!(
|
||||
"{neg}{}.eq_ignore_ascii_case({deref}{})",
|
||||
snippet_with_applicability(cx, left_span, "_", &mut app),
|
||||
snippet_with_applicability(cx, right_span, "_", &mut app)
|
||||
),
|
||||
app,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,10 +11,12 @@ use rustc_session::declare_lint_pass;
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, with x and unsigned integer, which are manual
|
||||
/// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, with x and unsigned integer, which may be manual
|
||||
/// reimplementations of `x.is_power_of_two()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Manual reimplementations of `is_power_of_two` increase code complexity for little benefit.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let a: u32 = 4;
|
||||
|
@ -27,7 +29,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.82.0"]
|
||||
pub MANUAL_IS_POWER_OF_TWO,
|
||||
complexity,
|
||||
pedantic,
|
||||
"manually reimplementing `is_power_of_two`"
|
||||
}
|
||||
|
||||
|
@ -41,7 +43,7 @@ impl LateLintPass<'_> for ManualIsPowerOfTwo {
|
|||
&& bin_op.node == BinOpKind::Eq
|
||||
{
|
||||
// a.count_ones() == 1
|
||||
if let ExprKind::MethodCall(method_name, reciever, _, _) = left.kind
|
||||
if let ExprKind::MethodCall(method_name, reciever, [], _) = left.kind
|
||||
&& method_name.ident.as_str() == "count_ones"
|
||||
&& let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind()
|
||||
&& check_lit(right, 1)
|
||||
|
@ -50,7 +52,7 @@ impl LateLintPass<'_> for ManualIsPowerOfTwo {
|
|||
}
|
||||
|
||||
// 1 == a.count_ones()
|
||||
if let ExprKind::MethodCall(method_name, reciever, _, _) = right.kind
|
||||
if let ExprKind::MethodCall(method_name, reciever, [], _) = right.kind
|
||||
&& method_name.ident.as_str() == "count_ones"
|
||||
&& let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind()
|
||||
&& check_lit(left, 1)
|
||||
|
|
|
@ -45,10 +45,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation {
|
|||
&& !expr.span.from_expansion()
|
||||
// Does not apply inside const because size_of_val is not cost in stable.
|
||||
&& !is_in_const_context(cx)
|
||||
&& let Some(receiver) = simplify(cx, left, right)
|
||||
&& let Some((receiver, refs_count)) = simplify(cx, left, right)
|
||||
{
|
||||
let ctxt = expr.span.ctxt();
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let deref = "*".repeat(refs_count - 1);
|
||||
let val_name = snippet_with_context(cx, receiver.span, ctxt, "slice", &mut app).0;
|
||||
let Some(sugg) = std_or_core(cx) else { return };
|
||||
|
||||
|
@ -58,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation {
|
|||
expr.span,
|
||||
"manual slice size calculation",
|
||||
"try",
|
||||
format!("{sugg}::mem::size_of_val({val_name})"),
|
||||
format!("{sugg}::mem::size_of_val({deref}{val_name})"),
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
@ -69,7 +70,7 @@ fn simplify<'tcx>(
|
|||
cx: &LateContext<'tcx>,
|
||||
expr1: &'tcx Expr<'tcx>,
|
||||
expr2: &'tcx Expr<'tcx>,
|
||||
) -> Option<&'tcx Expr<'tcx>> {
|
||||
) -> Option<(&'tcx Expr<'tcx>, usize)> {
|
||||
let expr1 = expr_or_init(cx, expr1);
|
||||
let expr2 = expr_or_init(cx, expr2);
|
||||
|
||||
|
@ -80,15 +81,16 @@ fn simplify_half<'tcx>(
|
|||
cx: &LateContext<'tcx>,
|
||||
expr1: &'tcx Expr<'tcx>,
|
||||
expr2: &'tcx Expr<'tcx>,
|
||||
) -> Option<&'tcx Expr<'tcx>> {
|
||||
) -> Option<(&'tcx Expr<'tcx>, usize)> {
|
||||
if !expr1.span.from_expansion()
|
||||
// expr1 is `[T1].len()`?
|
||||
&& let ExprKind::MethodCall(method_path, receiver, _, _) = expr1.kind
|
||||
&& let ExprKind::MethodCall(method_path, receiver, [], _) = expr1.kind
|
||||
&& method_path.ident.name == sym::len
|
||||
&& let receiver_ty = cx.typeck_results().expr_ty(receiver)
|
||||
&& let ty::Slice(ty1) = receiver_ty.peel_refs().kind()
|
||||
&& let (receiver_ty, refs_count) = clippy_utils::ty::walk_ptrs_ty_depth(receiver_ty)
|
||||
&& let ty::Slice(ty1) = receiver_ty.kind()
|
||||
// expr2 is `size_of::<T2>()`?
|
||||
&& let ExprKind::Call(func, _) = expr2.kind
|
||||
&& let ExprKind::Call(func, []) = expr2.kind
|
||||
&& let ExprKind::Path(ref func_qpath) = func.kind
|
||||
&& let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id)
|
||||
|
@ -96,7 +98,7 @@ fn simplify_half<'tcx>(
|
|||
// T1 == T2?
|
||||
&& *ty1 == ty2
|
||||
{
|
||||
Some(receiver)
|
||||
Some((receiver, refs_count))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -52,8 +52,8 @@ impl LateLintPass<'_> for ManualStringNew {
|
|||
}
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::Call(func, args) => {
|
||||
parse_call(cx, expr.span, func, args);
|
||||
ExprKind::Call(func, [arg]) => {
|
||||
parse_call(cx, expr.span, func, arg);
|
||||
},
|
||||
ExprKind::MethodCall(path_segment, receiver, ..) => {
|
||||
parse_method_call(cx, expr.span, path_segment, receiver);
|
||||
|
@ -93,20 +93,15 @@ fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegmen
|
|||
let method_arg_kind = &receiver.kind;
|
||||
if ["to_string", "to_owned", "into"].contains(&ident) && is_expr_kind_empty_str(method_arg_kind) {
|
||||
warn_then_suggest(cx, span);
|
||||
} else if let ExprKind::Call(func, args) = method_arg_kind {
|
||||
} else if let ExprKind::Call(func, [arg]) = method_arg_kind {
|
||||
// If our first argument is a function call itself, it could be an `unwrap`-like function.
|
||||
// E.g. String::try_from("hello").unwrap(), TryFrom::try_from("").expect("hello"), etc.
|
||||
parse_call(cx, span, func, args);
|
||||
parse_call(cx, span, func, arg);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to parse an expression as a function call, emitting the warning if necessary.
|
||||
fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
if args.len() != 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
let arg_kind = &args[0].kind;
|
||||
fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, arg: &Expr<'_>) {
|
||||
if let ExprKind::Path(qpath) = &func.kind {
|
||||
// String::from(...) or String::try_from(...)
|
||||
if let QPath::TypeRelative(ty, path_seg) = qpath
|
||||
|
@ -115,13 +110,13 @@ fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_
|
|||
&& let QPath::Resolved(_, path) = qpath
|
||||
&& let [path_seg] = path.segments
|
||||
&& path_seg.ident.name == sym::String
|
||||
&& is_expr_kind_empty_str(arg_kind)
|
||||
&& is_expr_kind_empty_str(&arg.kind)
|
||||
{
|
||||
warn_then_suggest(cx, span);
|
||||
} else if let QPath::Resolved(_, path) = qpath {
|
||||
// From::from(...) or TryFrom::try_from(...)
|
||||
if let [path_seg1, path_seg2] = path.segments
|
||||
&& is_expr_kind_empty_str(arg_kind)
|
||||
&& is_expr_kind_empty_str(&arg.kind)
|
||||
&& ((path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from)
|
||||
|| (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from))
|
||||
{
|
||||
|
|
|
@ -210,7 +210,7 @@ fn find_method_sugg_for_if_let<'tcx>(
|
|||
|
||||
// check that `while_let_on_iterator` lint does not trigger
|
||||
if keyword == "while"
|
||||
&& let ExprKind::MethodCall(method_path, ..) = let_expr.kind
|
||||
&& let ExprKind::MethodCall(method_path, _, [], _) = let_expr.kind
|
||||
&& method_path.ident.name == sym::next
|
||||
&& is_trait_method(cx, let_expr, sym::Iterator)
|
||||
{
|
||||
|
|
|
@ -21,10 +21,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
|
|||
// #[allow(unreachable_code)]
|
||||
// val,
|
||||
// };
|
||||
if let ExprKind::Call(match_fun, [try_arg, ..]) = scrutinee.kind
|
||||
if let ExprKind::Call(match_fun, [try_arg]) = scrutinee.kind
|
||||
&& let ExprKind::Path(ref match_fun_path) = match_fun.kind
|
||||
&& matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..))
|
||||
&& let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind
|
||||
&& let ExprKind::Call(err_fun, [err_arg]) = try_arg.kind
|
||||
&& is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr)
|
||||
&& let Some(return_ty) = find_return_type(cx, &expr.kind)
|
||||
{
|
||||
|
|
|
@ -58,7 +58,7 @@ pub(super) fn check(
|
|||
return;
|
||||
},
|
||||
// ? is a Call, makes sure not to rec *x?, but rather (*x)?
|
||||
ExprKind::Call(hir_callee, _) => matches!(
|
||||
ExprKind::Call(hir_callee, [_]) => matches!(
|
||||
hir_callee.kind,
|
||||
ExprKind::Path(QPath::LangItem(rustc_hir::LangItem::TryTraitBranch, ..))
|
||||
),
|
||||
|
|
|
@ -143,7 +143,7 @@ pub(super) fn check<'tcx>(
|
|||
cx,
|
||||
EXPECT_FUN_CALL,
|
||||
span_replace_word,
|
||||
format!("use of `{name}` followed by a function call"),
|
||||
format!("function call inside of `{name}`"),
|
||||
"try",
|
||||
format!("unwrap_or_else({closure_args} panic!({sugg}))"),
|
||||
applicability,
|
||||
|
@ -161,7 +161,7 @@ pub(super) fn check<'tcx>(
|
|||
cx,
|
||||
EXPECT_FUN_CALL,
|
||||
span_replace_word,
|
||||
format!("use of `{name}` followed by a function call"),
|
||||
format!("function call inside of `{name}`"),
|
||||
"try",
|
||||
format!("unwrap_or_else({closure_args} {{ panic!(\"{{}}\", {arg_root_snippet}) }})"),
|
||||
applicability,
|
||||
|
|
|
@ -106,9 +106,9 @@ fn is_method(
|
|||
|
||||
fn parent_is_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||
if let Some(expr) = get_parent_expr(cx, expr)
|
||||
&& is_trait_method(cx, expr, sym::Iterator)
|
||||
&& let ExprKind::MethodCall(path, _, _, _) = expr.kind
|
||||
&& let ExprKind::MethodCall(path, _, [_], _) = expr.kind
|
||||
&& path.ident.name == sym::map
|
||||
&& is_trait_method(cx, expr, sym::Iterator)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use rustc_ast::{LitKind, StrStyle};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Node, QPath, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::edition::Edition::Edition2021;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::MANUAL_C_STR_LITERALS;
|
||||
|
@ -25,6 +26,7 @@ pub(super) fn check_as_ptr<'tcx>(
|
|||
) {
|
||||
if let ExprKind::Lit(lit) = receiver.kind
|
||||
&& let LitKind::ByteStr(_, StrStyle::Cooked) | LitKind::Str(_, StrStyle::Cooked) = lit.node
|
||||
&& cx.tcx.sess.edition() >= Edition2021
|
||||
&& let casts_removed = peel_ptr_cast_ancestors(cx, expr)
|
||||
&& !get_parent_expr(cx, casts_removed).is_some_and(
|
||||
|parent| matches!(parent.kind, ExprKind::Call(func, _) if is_c_str_function(cx, func).is_some()),
|
||||
|
@ -66,6 +68,7 @@ fn is_c_str_function(cx: &LateContext<'_>, func: &Expr<'_>) -> Option<Symbol> {
|
|||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>], msrv: &Msrv) {
|
||||
if let Some(fn_name) = is_c_str_function(cx, func)
|
||||
&& let [arg] = args
|
||||
&& cx.tcx.sess.edition() >= Edition2021
|
||||
&& msrv.meets(msrvs::C_STR_LITERALS)
|
||||
{
|
||||
match fn_name.as_str() {
|
||||
|
@ -84,7 +87,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args
|
|||
|
||||
/// Checks `CStr::from_ptr(b"foo\0".as_ptr().cast())`
|
||||
fn check_from_ptr(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) {
|
||||
if let ExprKind::MethodCall(method, lit, ..) = peel_ptr_cast(arg).kind
|
||||
if let ExprKind::MethodCall(method, lit, [], _) = peel_ptr_cast(arg).kind
|
||||
&& method.ident.name == sym::as_ptr
|
||||
&& !lit.span.from_expansion()
|
||||
&& let ExprKind::Lit(lit) = lit.kind
|
||||
|
|
|
@ -68,8 +68,7 @@ enum MinMax {
|
|||
|
||||
fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
|
||||
// `T::max_value()` `T::min_value()` inherent methods
|
||||
if let hir::ExprKind::Call(func, args) = &expr.kind
|
||||
&& args.is_empty()
|
||||
if let hir::ExprKind::Call(func, []) = &expr.kind
|
||||
&& let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind
|
||||
{
|
||||
match segment.ident.as_str() {
|
||||
|
|
|
@ -86,9 +86,8 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
|
|||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Call(call, args) => {
|
||||
hir::ExprKind::Call(call, [arg]) => {
|
||||
if let hir::ExprKind::Path(qpath) = call.kind
|
||||
&& let [arg] = args
|
||||
&& ident_eq(name, arg)
|
||||
{
|
||||
handle_path(cx, call, &qpath, e, recv);
|
||||
|
|
|
@ -4046,7 +4046,7 @@ declare_clippy_lint! {
|
|||
/// Checks the usage of `.get().is_some()` or `.get().is_none()` on std map types.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It can be done in one call with `.contains()`/`.contains_keys()`.
|
||||
/// It can be done in one call with `.contains()`/`.contains_key()`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
|
@ -5182,7 +5182,7 @@ impl ShouldImplTraitCase {
|
|||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
|
||||
static TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
|
||||
ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
|
||||
ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
|
||||
ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
|
||||
|
|
|
@ -321,7 +321,10 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
|
|||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
// Check function calls on our collection
|
||||
if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind {
|
||||
if method_name.ident.name == sym!(collect) && is_trait_method(self.cx, expr, sym::Iterator) {
|
||||
if args.is_empty()
|
||||
&& method_name.ident.name == sym!(collect)
|
||||
&& is_trait_method(self.cx, expr, sym::Iterator)
|
||||
{
|
||||
self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv));
|
||||
self.visit_expr(recv);
|
||||
return;
|
||||
|
|
|
@ -183,7 +183,7 @@ pub(super) fn check<'tcx>(
|
|||
cx,
|
||||
OR_FUN_CALL,
|
||||
span_replace_word,
|
||||
format!("use of `{name}` followed by a function call"),
|
||||
format!("function call inside of `{name}`"),
|
||||
"try",
|
||||
format!("{name}_{suffix}({sugg})"),
|
||||
app,
|
||||
|
@ -259,7 +259,7 @@ fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>)
|
|||
|
||||
if body.params.is_empty()
|
||||
&& let hir::Expr { kind, .. } = &body.value
|
||||
&& let hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, self_arg, _, _) = kind
|
||||
&& let hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, self_arg, [], _) = kind
|
||||
&& ident.name == sym::to_string
|
||||
&& let hir::Expr { kind, .. } = self_arg
|
||||
&& let hir::ExprKind::Lit(lit) = kind
|
||||
|
|
|
@ -43,7 +43,8 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<
|
|||
for_each_local_use_after_expr(cx, local_id, call.hir_id, |expr| {
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
let data = if let ExprKind::MethodCall(segment, recv, args, span) = parent.kind {
|
||||
if segment.ident.name == sym!(parse)
|
||||
if args.is_empty()
|
||||
&& segment.ident.name == sym!(parse)
|
||||
&& let parse_result_ty = cx.typeck_results().expr_ty(parent)
|
||||
&& is_type_diagnostic_item(cx, parse_result_ty, sym::Result)
|
||||
&& let ty::Adt(_, substs) = parse_result_ty.kind()
|
||||
|
|
|
@ -10,7 +10,7 @@ use rustc_middle::mir::{Location, START_BLOCK};
|
|||
use rustc_span::sym;
|
||||
|
||||
fn is_unwrap_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::MethodCall(path, receiver, ..) = expr.kind
|
||||
if let ExprKind::MethodCall(path, receiver, [], _) = expr.kind
|
||||
&& path.ident.name == sym::unwrap
|
||||
{
|
||||
is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::Result)
|
||||
|
|
|
@ -34,14 +34,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'
|
|||
}
|
||||
|
||||
fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
if let ExprKind::Call(f, args) = expr.kind
|
||||
if let ExprKind::Call(f, [arg]) = expr.kind
|
||||
&& let ExprKind::Path(ref path) = f.kind
|
||||
&& let Some(ctor_call_id) = cx.qpath_res(path, f.hir_id).opt_def_id()
|
||||
&& is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Current), ctor_call_id)
|
||||
{
|
||||
// check if argument of `SeekFrom::Current` is `0`
|
||||
if args.len() == 1
|
||||
&& let ExprKind::Lit(lit) = args[0].kind
|
||||
if let ExprKind::Lit(lit) = arg.kind
|
||||
&& let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node
|
||||
{
|
||||
return true;
|
||||
|
|
|
@ -26,12 +26,11 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
if let Some(seek_trait_id) = cx.tcx.get_diagnostic_item(sym::IoSeek)
|
||||
&& implements_trait(cx, ty, seek_trait_id, &[])
|
||||
&& let ExprKind::Call(func, args1) = arg.kind
|
||||
&& let ExprKind::Call(func, [arg]) = arg.kind
|
||||
&& let ExprKind::Path(ref path) = func.kind
|
||||
&& let Some(ctor_call_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
|
||||
&& is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Start), ctor_call_id)
|
||||
&& args1.len() == 1
|
||||
&& let ExprKind::Lit(lit) = args1[0].kind
|
||||
&& let ExprKind::Lit(lit) = arg.kind
|
||||
&& let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node
|
||||
{
|
||||
let method_call_span = expr.span.with_lo(name_span.lo());
|
||||
|
|
|
@ -27,7 +27,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::
|
|||
}
|
||||
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind
|
||||
&& let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind
|
||||
&& let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind
|
||||
&& path_segment.ident.name == rustc_span::sym::to_string
|
||||
&& (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
|
||||
{
|
||||
|
|
|
@ -26,7 +26,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::
|
|||
}
|
||||
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind
|
||||
&& let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind
|
||||
&& let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind
|
||||
&& path_segment.ident.name == rustc_span::sym::to_string
|
||||
&& (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
|
||||
{
|
||||
|
|
|
@ -333,7 +333,7 @@ fn parse_iter_usage<'tcx>(
|
|||
kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)),
|
||||
..
|
||||
},
|
||||
_,
|
||||
[_],
|
||||
) => {
|
||||
let parent_span = e.span.parent_callsite().unwrap();
|
||||
if parent_span.ctxt() == ctxt {
|
||||
|
|
|
@ -9,8 +9,7 @@ use super::UNINIT_ASSUMED_INIT;
|
|||
|
||||
/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter)
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
|
||||
if let hir::ExprKind::Call(callee, args) = recv.kind
|
||||
&& args.is_empty()
|
||||
if let hir::ExprKind::Call(callee, []) = recv.kind
|
||||
&& is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit)
|
||||
&& !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr))
|
||||
{
|
||||
|
|
|
@ -50,7 +50,7 @@ pub(super) fn check(
|
|||
),
|
||||
"replace this with",
|
||||
suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ use clippy_utils::ty::implements_trait;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, GenericArgKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::iter;
|
||||
|
|
|
@ -86,12 +86,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str,
|
|||
// changing the type, then we can move forward.
|
||||
&& rcv_ty.peel_refs() == res_ty.peel_refs()
|
||||
&& let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let hir::ExprKind::MethodCall(segment, _, args, _) = parent.kind
|
||||
// Check that it only has one argument.
|
||||
&& let hir::ExprKind::MethodCall(segment, _, [arg], _) = parent.kind
|
||||
&& segment.ident.span != expr.span
|
||||
// We check that the called method name is `map`.
|
||||
&& segment.ident.name == sym::map
|
||||
// And that it only has one argument.
|
||||
&& let [arg] = args
|
||||
&& is_calling_clone(cx, arg)
|
||||
// And that we are not recommending recv.clone() over Arc::clone() or similar
|
||||
&& !should_call_clone_as_function(cx, rcv_ty)
|
||||
|
|
|
@ -139,7 +139,7 @@ fn assert_len_expr<'hir>(
|
|||
&& let ExprKind::Binary(bin_op, left, right) = &condition.kind
|
||||
|
||||
&& let Some((cmp, asserted_len, slice_len)) = len_comparison(*bin_op, left, right)
|
||||
&& let ExprKind::MethodCall(method, recv, ..) = &slice_len.kind
|
||||
&& let ExprKind::MethodCall(method, recv, [], _) = &slice_len.kind
|
||||
&& cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
|
||||
&& method.ident.name == sym::len
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
|||
| hir::ItemKind::Trait(..)
|
||||
| hir::ItemKind::TraitAlias(..)
|
||||
| hir::ItemKind::TyAlias(..)
|
||||
| hir::ItemKind::Union(..) => {}
|
||||
| hir::ItemKind::Union(..) => {},
|
||||
hir::ItemKind::ExternCrate(..)
|
||||
| hir::ItemKind::ForeignMod { .. }
|
||||
| hir::ItemKind::GlobalAsm(..)
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::trait_ref_of_method;
|
||||
use clippy_utils::ty::InteriorMut;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
@ -132,8 +133,14 @@ impl<'tcx> MutableKeyType<'tcx> {
|
|||
)
|
||||
{
|
||||
let subst_ty = args.type_at(0);
|
||||
if self.interior_mut.is_interior_mut_ty(cx, subst_ty) {
|
||||
span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
|
||||
if let Some(chain) = self.interior_mut.interior_mut_ty_chain(cx, subst_ty) {
|
||||
span_lint_and_then(cx, MUTABLE_KEY_TYPE, span, "mutable key type", |diag| {
|
||||
for ty in chain.iter().rev() {
|
||||
diag.note(with_forced_trimmed_paths!(format!(
|
||||
"... because it contains `{ty}`, which has interior mutability"
|
||||
)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
|
|||
if let ExprKind::Path(ref path) = fn_expr.kind {
|
||||
check_arguments(
|
||||
cx,
|
||||
arguments.iter().collect(),
|
||||
&mut arguments.iter(),
|
||||
cx.typeck_results().expr_ty(fn_expr),
|
||||
&rustc_hir_pretty::qpath_to_string(&cx.tcx, path),
|
||||
"function",
|
||||
|
@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
|
|||
let method_type = cx.tcx.type_of(def_id).instantiate(cx.tcx, args);
|
||||
check_arguments(
|
||||
cx,
|
||||
iter::once(receiver).chain(arguments.iter()).collect(),
|
||||
&mut iter::once(receiver).chain(arguments.iter()),
|
||||
method_type,
|
||||
path.ident.as_str(),
|
||||
"method",
|
||||
|
@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
|
|||
|
||||
fn check_arguments<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
arguments: Vec<&Expr<'_>>,
|
||||
arguments: &mut dyn Iterator<Item = &'tcx Expr<'tcx>>,
|
||||
type_definition: Ty<'tcx>,
|
||||
name: &str,
|
||||
fn_kind: &str,
|
||||
|
|
|
@ -96,10 +96,6 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> {
|
|||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::If(..) => {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||
if adj
|
||||
|
|
|
@ -64,7 +64,7 @@ fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item
|
|||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(move |(bound_pos, bound)| match bound {
|
||||
&GenericBound::Trait(ref trait_bound) => Some(Bound {
|
||||
GenericBound::Trait(trait_bound) => Some(Bound {
|
||||
param,
|
||||
ident,
|
||||
trait_bound,
|
||||
|
|
|
@ -281,7 +281,7 @@ fn self_cmp_call<'tcx>(
|
|||
needs_fully_qualified: &mut bool,
|
||||
) -> bool {
|
||||
match cmp_expr.kind {
|
||||
ExprKind::Call(path, [_self, _other]) => path_res(cx, path)
|
||||
ExprKind::Call(path, [_, _]) => path_res(cx, path)
|
||||
.opt_def_id()
|
||||
.is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)),
|
||||
ExprKind::MethodCall(_, _, [_other], ..) => {
|
||||
|
|
|
@ -71,7 +71,7 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit
|
|||
if let ExprKind::Call(func, [arg]) = expr.kind
|
||||
&& let ExprKind::Path(qpath) = &func.kind
|
||||
&& let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id()
|
||||
&& let ExprKind::MethodCall(rcv_path, receiver, _, _) = &arg.kind
|
||||
&& let ExprKind::MethodCall(rcv_path, receiver, [], _) = &arg.kind
|
||||
&& rcv_path.ident.name.as_str() == "get"
|
||||
{
|
||||
let fn_name = cx.tcx.item_name(def_id);
|
||||
|
|
|
@ -106,7 +106,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
return is_signum(cx, child_expr);
|
||||
}
|
||||
|
||||
if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind
|
||||
if let ExprKind::MethodCall(method_name, self_arg, [], _) = expr.kind
|
||||
&& sym!(signum) == method_name.ident.name
|
||||
// Check that the receiver of the signum() is a float (expressions[0] is the receiver of
|
||||
// the method call)
|
||||
|
|
|
@ -37,7 +37,7 @@ declare_clippy_lint! {
|
|||
/// // or
|
||||
/// let path_buf = PathBuf::new().join("foo");
|
||||
/// ```
|
||||
#[clippy::version = "1.81.0"]
|
||||
#[clippy::version = "1.82.0"]
|
||||
pub PATHBUF_INIT_THEN_PUSH,
|
||||
restriction,
|
||||
"`push` immediately after `PathBuf` creation"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::visitors::contains_unsafe_block;
|
||||
use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local};
|
||||
use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core};
|
||||
use hir::LifetimeName;
|
||||
use rustc_errors::{Applicability, MultiSpan};
|
||||
use rustc_hir::hir_id::{HirId, HirIdMap};
|
||||
|
@ -294,14 +294,16 @@ fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||
};
|
||||
|
||||
for &arg_idx in arg_indices {
|
||||
if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
|
||||
if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg))
|
||||
&& let Some(std_or_core) = std_or_core(cx)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
INVALID_NULL_PTR_USAGE,
|
||||
arg.span,
|
||||
"pointer must be non-null",
|
||||
"change this to",
|
||||
"core::ptr::NonNull::dangling().as_ptr()".to_string(),
|
||||
format!("{std_or_core}::ptr::NonNull::dangling().as_ptr()"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ fn expr_as_ptr_offset_call<'tcx>(
|
|||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> {
|
||||
if let ExprKind::MethodCall(path_segment, arg_0, [arg_1, ..], _) = &expr.kind {
|
||||
if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind {
|
||||
if is_expr_ty_raw_ptr(cx, arg_0) {
|
||||
if path_segment.ident.name == sym::offset {
|
||||
return Some((arg_0, arg_1, Method::Offset));
|
||||
|
|
|
@ -206,12 +206,11 @@ fn expr_return_none_or_err(
|
|||
sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
|
||||
_ => false,
|
||||
},
|
||||
ExprKind::Call(call_expr, args_expr) => {
|
||||
ExprKind::Call(call_expr, [arg]) => {
|
||||
if smbl == sym::Result
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = &call_expr.kind
|
||||
&& let Some(segment) = path.segments.first()
|
||||
&& let Some(err_sym) = err_sym
|
||||
&& let Some(arg) = args_expr.first()
|
||||
&& let ExprKind::Path(QPath::Resolved(_, arg_path)) = &arg.kind
|
||||
&& let Some(PathSegment { ident, .. }) = arg_path.segments.first()
|
||||
{
|
||||
|
@ -241,7 +240,7 @@ fn expr_return_none_or_err(
|
|||
fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr)
|
||||
&& !is_else_clause(cx.tcx, expr)
|
||||
&& let ExprKind::MethodCall(segment, caller, ..) = &cond.kind
|
||||
&& let ExprKind::MethodCall(segment, caller, [], _) = &cond.kind
|
||||
&& let caller_ty = cx.typeck_results().expr_ty(caller)
|
||||
&& let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then)
|
||||
&& (is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block))
|
||||
|
@ -332,7 +331,7 @@ impl QuestionMark {
|
|||
|
||||
fn is_try_block(cx: &LateContext<'_>, bl: &Block<'_>) -> bool {
|
||||
if let Some(expr) = bl.expr
|
||||
&& let ExprKind::Call(callee, _) = expr.kind
|
||||
&& let ExprKind::Call(callee, [_]) = expr.kind
|
||||
{
|
||||
is_path_lang_item(cx, callee, LangItem::TryTraitFromOutput)
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::source::{SpanRangeExt, snippet_opt};
|
||||
use rustc_ast::ast::{Expr, ExprKind};
|
||||
use rustc_ast::token::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -71,6 +71,23 @@ impl RawStrings {
|
|||
|
||||
impl EarlyLintPass for RawStrings {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::FormatArgs(format_args) = &expr.kind
|
||||
&& !in_external_macro(cx.sess(), format_args.span)
|
||||
&& format_args.span.check_source_text(cx, |src| src.starts_with('r'))
|
||||
&& let Some(str) = snippet_opt(cx.sess(), format_args.span)
|
||||
&& let count_hash = str.bytes().skip(1).take_while(|b| *b == b'#').count()
|
||||
&& let Some(str) = str.get(count_hash + 2..str.len() - count_hash - 1)
|
||||
{
|
||||
self.check_raw_string(
|
||||
cx,
|
||||
str,
|
||||
format_args.span,
|
||||
"r",
|
||||
u8::try_from(count_hash).unwrap(),
|
||||
"string",
|
||||
);
|
||||
}
|
||||
|
||||
if let ExprKind::Lit(lit) = expr.kind
|
||||
&& let (prefix, max) = match lit.kind {
|
||||
LitKind::StrRaw(max) => ("r", max),
|
||||
|
@ -81,94 +98,105 @@ impl EarlyLintPass for RawStrings {
|
|||
&& !in_external_macro(cx.sess(), expr.span)
|
||||
&& expr.span.check_source_text(cx, |src| src.starts_with(prefix))
|
||||
{
|
||||
let str = lit.symbol.as_str();
|
||||
let descr = lit.kind.descr();
|
||||
self.check_raw_string(cx, lit.symbol.as_str(), expr.span, prefix, max, lit.kind.descr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !str.contains(['\\', '"']) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_RAW_STRINGS,
|
||||
expr.span,
|
||||
"unnecessary raw string literal",
|
||||
|diag| {
|
||||
let (start, end) = hash_spans(expr.span, prefix.len(), 0, max);
|
||||
impl RawStrings {
|
||||
fn check_raw_string(
|
||||
&mut self,
|
||||
cx: &EarlyContext<'_>,
|
||||
str: &str,
|
||||
lit_span: Span,
|
||||
prefix: &str,
|
||||
max: u8,
|
||||
descr: &str,
|
||||
) {
|
||||
if !str.contains(['\\', '"']) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_RAW_STRINGS,
|
||||
lit_span,
|
||||
"unnecessary raw string literal",
|
||||
|diag| {
|
||||
let (start, end) = hash_spans(lit_span, prefix.len(), 0, max);
|
||||
|
||||
// BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text
|
||||
let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1);
|
||||
let start = start.with_lo(r_pos);
|
||||
// BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text
|
||||
let r_pos = lit_span.lo() + BytePos::from_usize(prefix.len() - 1);
|
||||
let start = start.with_lo(r_pos);
|
||||
|
||||
let mut remove = vec![(start, String::new())];
|
||||
// avoid debug ICE from empty suggestions
|
||||
if !end.is_empty() {
|
||||
remove.push((end, String::new()));
|
||||
}
|
||||
|
||||
diag.multipart_suggestion_verbose(
|
||||
format!("use a plain {descr} literal instead"),
|
||||
remove,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let mut req = {
|
||||
let mut following_quote = false;
|
||||
let mut req = 0;
|
||||
// `once` so a raw string ending in hashes is still checked
|
||||
let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| {
|
||||
match b {
|
||||
b'"' if !following_quote => (following_quote, req) = (true, 1),
|
||||
b'#' => req += u8::from(following_quote),
|
||||
_ => {
|
||||
if following_quote {
|
||||
following_quote = false;
|
||||
|
||||
if req == max {
|
||||
return ControlFlow::Break(req);
|
||||
}
|
||||
|
||||
return ControlFlow::Continue(acc.max(req));
|
||||
}
|
||||
},
|
||||
let mut remove = vec![(start, String::new())];
|
||||
// avoid debug ICE from empty suggestions
|
||||
if !end.is_empty() {
|
||||
remove.push((end, String::new()));
|
||||
}
|
||||
|
||||
ControlFlow::Continue(acc)
|
||||
});
|
||||
|
||||
match num {
|
||||
ControlFlow::Continue(num) | ControlFlow::Break(num) => num,
|
||||
}
|
||||
};
|
||||
if self.allow_one_hash_in_raw_strings {
|
||||
req = req.max(1);
|
||||
diag.multipart_suggestion_verbose(
|
||||
format!("use a plain {descr} literal instead"),
|
||||
remove,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) {
|
||||
return;
|
||||
}
|
||||
if req < max {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_RAW_STRING_HASHES,
|
||||
expr.span,
|
||||
"unnecessary hashes around raw string literal",
|
||||
|diag| {
|
||||
let (start, end) = hash_spans(expr.span, prefix.len(), req, max);
|
||||
}
|
||||
|
||||
let message = match max - req {
|
||||
_ if req == 0 => format!("remove all the hashes around the {descr} literal"),
|
||||
1 => format!("remove one hash from both sides of the {descr} literal"),
|
||||
n => format!("remove {n} hashes from both sides of the {descr} literal"),
|
||||
};
|
||||
let mut req = {
|
||||
let mut following_quote = false;
|
||||
let mut req = 0;
|
||||
// `once` so a raw string ending in hashes is still checked
|
||||
let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| {
|
||||
match b {
|
||||
b'"' if !following_quote => (following_quote, req) = (true, 1),
|
||||
b'#' => req += u8::from(following_quote),
|
||||
_ => {
|
||||
if following_quote {
|
||||
following_quote = false;
|
||||
|
||||
diag.multipart_suggestion(
|
||||
message,
|
||||
vec![(start, String::new()), (end, String::new())],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
if req == max {
|
||||
return ControlFlow::Break(req);
|
||||
}
|
||||
|
||||
return ControlFlow::Continue(acc.max(req));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
ControlFlow::Continue(acc)
|
||||
});
|
||||
|
||||
match num {
|
||||
ControlFlow::Continue(num) | ControlFlow::Break(num) => num,
|
||||
}
|
||||
};
|
||||
if self.allow_one_hash_in_raw_strings {
|
||||
req = req.max(1);
|
||||
}
|
||||
if req < max {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_RAW_STRING_HASHES,
|
||||
lit_span,
|
||||
"unnecessary hashes around raw string literal",
|
||||
|diag| {
|
||||
let (start, end) = hash_spans(lit_span, prefix.len(), req, max);
|
||||
|
||||
let message = match max - req {
|
||||
_ if req == 0 => format!("remove all the hashes around the {descr} literal"),
|
||||
1 => format!("remove one hash from both sides of the {descr} literal"),
|
||||
n => format!("remove {n} hashes from both sides of the {descr} literal"),
|
||||
};
|
||||
|
||||
diag.multipart_suggestion(
|
||||
message,
|
||||
vec![(start, String::new()), (end, String::new())],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,11 +65,11 @@ impl LateLintPass<'_> for RcCloneInVecInit {
|
|||
|
||||
fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String {
|
||||
format!(
|
||||
r#"{{
|
||||
r"{{
|
||||
{indent} let mut v = Vec::with_capacity({len});
|
||||
{indent} (0..{len}).for_each(|_| v.push({elem}));
|
||||
{indent} v
|
||||
{indent}}}"#
|
||||
{indent}}}"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use clippy_utils::source::SpanRangeExt;
|
|||
use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths};
|
||||
use rustc_ast::ast::{LitKind, StrStyle};
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind};
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
@ -55,6 +55,44 @@ declare_clippy_lint! {
|
|||
"trivial regular expressions"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Checks for [regex](https://crates.io/crates/regex) compilation inside a loop with a literal.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Compiling a regex is a much more expensive operation than using one, and a compiled regex can be used multiple times.
|
||||
/// This is documented as an antipattern [on the regex documentation](https://docs.rs/regex/latest/regex/#avoid-re-compiling-regexes-especially-in-a-loop)
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let haystacks = [""];
|
||||
/// # const MY_REGEX: &str = "a.b";
|
||||
/// for haystack in haystacks {
|
||||
/// let regex = regex::Regex::new(MY_REGEX).unwrap();
|
||||
/// if regex.is_match(haystack) {
|
||||
/// // Perform operation
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// can be replaced with
|
||||
/// ```no_run
|
||||
/// # let haystacks = [""];
|
||||
/// # const MY_REGEX: &str = "a.b";
|
||||
/// let regex = regex::Regex::new(MY_REGEX).unwrap();
|
||||
/// for haystack in haystacks {
|
||||
/// if regex.is_match(haystack) {
|
||||
/// // Perform operation
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.83.0"]
|
||||
pub REGEX_CREATION_IN_LOOPS,
|
||||
perf,
|
||||
"regular expression compilation performed in a loop"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum RegexKind {
|
||||
Unicode,
|
||||
|
@ -66,9 +104,10 @@ enum RegexKind {
|
|||
#[derive(Default)]
|
||||
pub struct Regex {
|
||||
definitions: DefIdMap<RegexKind>,
|
||||
loop_stack: Vec<(OwnerId, Span)>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
|
||||
impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX, REGEX_CREATION_IN_LOOPS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Regex {
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
||||
|
@ -99,12 +138,34 @@ impl<'tcx> LateLintPass<'tcx> for Regex {
|
|||
&& let Some(def_id) = path_def_id(cx, fun)
|
||||
&& let Some(regex_kind) = self.definitions.get(&def_id)
|
||||
{
|
||||
if let Some(&(loop_item_id, loop_span)) = self.loop_stack.last()
|
||||
&& loop_item_id == fun.hir_id.owner
|
||||
&& (matches!(arg.kind, ExprKind::Lit(_)) || const_str(cx, arg).is_some())
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
REGEX_CREATION_IN_LOOPS,
|
||||
fun.span,
|
||||
"compiling a regex in a loop",
|
||||
Some(loop_span),
|
||||
"move the regex construction outside this loop",
|
||||
);
|
||||
}
|
||||
|
||||
match regex_kind {
|
||||
RegexKind::Unicode => check_regex(cx, arg, true),
|
||||
RegexKind::UnicodeSet => check_set(cx, arg, true),
|
||||
RegexKind::Bytes => check_regex(cx, arg, false),
|
||||
RegexKind::BytesSet => check_set(cx, arg, false),
|
||||
}
|
||||
} else if let ExprKind::Loop(block, _, _, span) = expr.kind {
|
||||
self.loop_stack.push((block.hir_id.owner, span));
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if matches!(expr.kind, ExprKind::Loop(..)) {
|
||||
self.loop_stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -357,7 +357,7 @@ fn check_final_expr<'tcx>(
|
|||
|
||||
let replacement = if let Some(inner_expr) = inner {
|
||||
// if desugar of `do yeet`, don't lint
|
||||
if let ExprKind::Call(path_expr, _) = inner_expr.kind
|
||||
if let ExprKind::Call(path_expr, [_]) = inner_expr.kind
|
||||
&& let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind
|
||||
{
|
||||
return;
|
||||
|
|
|
@ -421,11 +421,10 @@ fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> {
|
|||
}
|
||||
|
||||
fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_>) -> bool {
|
||||
if let hir::ExprKind::Call(fun, args) = expr.kind
|
||||
if let hir::ExprKind::Call(fun, [first_arg]) = expr.kind
|
||||
&& let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind
|
||||
&& let Res::Def(DefKind::Fn, did) = fun_path.res
|
||||
&& lcx.tcx.is_diagnostic_item(sym::mem_drop, did)
|
||||
&& let [first_arg, ..] = args
|
||||
{
|
||||
let has_ident = |local_expr: &hir::Expr<'_>| {
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind
|
||||
|
|
|
@ -34,7 +34,7 @@ declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]);
|
|||
|
||||
fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> {
|
||||
match expr.kind {
|
||||
ExprKind::Call(count_func, _func_args) => {
|
||||
ExprKind::Call(count_func, _) => {
|
||||
if !inverted
|
||||
&& let ExprKind::Path(ref count_func_qpath) = count_func.kind
|
||||
&& let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id()
|
||||
|
|
|
@ -152,7 +152,7 @@ impl SlowVectorInit {
|
|||
&& is_path_diagnostic_item(cx, func, sym::vec_with_capacity)
|
||||
{
|
||||
Some(InitializedSize::Initialized(len_expr))
|
||||
} else if matches!(expr.kind, ExprKind::Call(func, _) if is_path_diagnostic_item(cx, func, sym::vec_new)) {
|
||||
} else if matches!(expr.kind, ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::vec_new)) {
|
||||
Some(InitializedSize::Uninitialized)
|
||||
} else {
|
||||
None
|
||||
|
@ -268,7 +268,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> {
|
|||
|
||||
/// Returns `true` if give expression is `repeat(0).take(...)`
|
||||
fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
if let ExprKind::MethodCall(take_path, recv, [len_arg, ..], _) = expr.kind
|
||||
if let ExprKind::MethodCall(take_path, recv, [len_arg], _) = expr.kind
|
||||
&& take_path.ident.name == sym!(take)
|
||||
// Check that take is applied to `repeat(0)`
|
||||
&& self.is_repeat_zero(recv)
|
||||
|
|
|
@ -253,18 +253,17 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
|
|||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
use rustc_ast::LitKind;
|
||||
|
||||
if let ExprKind::Call(fun, args) = e.kind
|
||||
if let ExprKind::Call(fun, [bytes_arg]) = e.kind
|
||||
// Find std::str::converts::from_utf8
|
||||
&& is_path_diagnostic_item(cx, fun, sym::str_from_utf8)
|
||||
|
||||
// Find string::as_bytes
|
||||
&& let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind
|
||||
&& let ExprKind::AddrOf(BorrowKind::Ref, _, args) = bytes_arg.kind
|
||||
&& let ExprKind::Index(left, right, _) = args.kind
|
||||
&& let (method_names, expressions, _) = method_calls(left, 1)
|
||||
&& method_names.len() == 1
|
||||
&& method_names == [sym!(as_bytes)]
|
||||
&& expressions.len() == 1
|
||||
&& expressions[0].1.is_empty()
|
||||
&& method_names[0] == sym!(as_bytes)
|
||||
|
||||
// Check for slicer
|
||||
&& let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), _, _) = right.kind
|
||||
|
@ -393,7 +392,7 @@ impl<'tcx> LateLintPass<'tcx> for StrToString {
|
|||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
|
||||
if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind
|
||||
&& path.ident.name == sym::to_string
|
||||
&& let ty = cx.typeck_results().expr_ty(self_arg)
|
||||
&& let ty::Ref(_, ty, ..) = ty.kind()
|
||||
|
@ -449,7 +448,7 @@ impl<'tcx> LateLintPass<'tcx> for StringToString {
|
|||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
|
||||
if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind
|
||||
&& path.ident.name == sym::to_string
|
||||
&& let ty = cx.typeck_results().expr_ty(self_arg)
|
||||
&& is_type_lang_item(cx, ty, LangItem::String)
|
||||
|
|
|
@ -51,9 +51,8 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome {
|
|||
None
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Call(to_digits_call, to_digit_args) => {
|
||||
if let [char_arg, radix_arg] = *to_digit_args
|
||||
&& let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind
|
||||
hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => {
|
||||
if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind
|
||||
&& let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id)
|
||||
&& let Some(to_digits_def_id) = to_digits_call_res.opt_def_id()
|
||||
&& match_def_path(cx, to_digits_def_id, &[
|
||||
|
|
|
@ -10,7 +10,7 @@ use rustc_data_structures::unhash::UnhashMap;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{
|
||||
GenericArg, GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath,
|
||||
GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath,
|
||||
TraitBoundModifier, TraitItem, TraitRef, Ty, TyKind, WherePredicate,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -152,7 +152,10 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
|
|||
.filter_map(get_trait_info_from_bound)
|
||||
.for_each(|(trait_item_res, trait_item_segments, span)| {
|
||||
if let Some(self_segments) = self_bounds_map.get(&trait_item_res) {
|
||||
if SpanlessEq::new(cx).eq_path_segments(self_segments, trait_item_segments) {
|
||||
if SpanlessEq::new(cx)
|
||||
.paths_by_resolution()
|
||||
.eq_path_segments(self_segments, trait_item_segments)
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
TRAIT_DUPLICATION_IN_BOUNDS,
|
||||
|
@ -302,7 +305,7 @@ impl TraitBounds {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_trait_bound_duplication(cx: &LateContext<'_>, generics: &'_ Generics<'_>) {
|
||||
fn check_trait_bound_duplication<'tcx>(cx: &LateContext<'tcx>, generics: &'_ Generics<'tcx>) {
|
||||
if generics.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
@ -314,6 +317,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, generics: &'_ Generics<'_
|
|||
// |
|
||||
// collects each of these where clauses into a set keyed by generic name and comparable trait
|
||||
// eg. (T, Clone)
|
||||
#[expect(clippy::mutable_key_type)]
|
||||
let where_predicates = generics
|
||||
.predicates
|
||||
.iter()
|
||||
|
@ -367,11 +371,27 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, generics: &'_ Generics<'_
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
struct ComparableTraitRef(Res, Vec<Res>);
|
||||
impl Default for ComparableTraitRef {
|
||||
fn default() -> Self {
|
||||
Self(Res::Err, Vec::new())
|
||||
struct ComparableTraitRef<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
trait_ref: &'tcx TraitRef<'tcx>,
|
||||
modifier: TraitBoundModifier,
|
||||
}
|
||||
|
||||
impl PartialEq for ComparableTraitRef<'_, '_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.modifier == other.modifier
|
||||
&& SpanlessEq::new(self.cx)
|
||||
.paths_by_resolution()
|
||||
.eq_path(self.trait_ref.path, other.trait_ref.path)
|
||||
}
|
||||
}
|
||||
impl Eq for ComparableTraitRef<'_, '_> {}
|
||||
impl Hash for ComparableTraitRef<'_, '_> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
let mut s = SpanlessHash::new(self.cx).paths_by_resolution();
|
||||
s.hash_path(self.trait_ref.path);
|
||||
state.write_u64(s.finish());
|
||||
self.modifier.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,69 +412,41 @@ 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(
|
||||
trait_ref.path.res,
|
||||
trait_ref
|
||||
.path
|
||||
.segments
|
||||
.iter()
|
||||
.filter_map(|segment| {
|
||||
// get trait bound type arguments
|
||||
Some(segment.args?.args.iter().filter_map(|arg| {
|
||||
if let GenericArg::Type(ty) = arg {
|
||||
return get_ty_res(**ty);
|
||||
}
|
||||
None
|
||||
}))
|
||||
})
|
||||
.flatten()
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn rollup_traits(
|
||||
cx: &LateContext<'_>,
|
||||
bounds: &[GenericBound<'_>],
|
||||
fn rollup_traits<'cx, 'tcx>(
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
bounds: &'tcx [GenericBound<'tcx>],
|
||||
msg: &'static str,
|
||||
) -> Vec<(ComparableTraitRef, Span)> {
|
||||
) -> Vec<(ComparableTraitRef<'cx, 'tcx>, Span)> {
|
||||
// Source order is needed for joining spans
|
||||
let mut map = FxIndexMap::default();
|
||||
let mut repeated_res = false;
|
||||
|
||||
let only_comparable_trait_refs = |bound: &GenericBound<'_>| {
|
||||
let only_comparable_trait_refs = |bound: &'tcx GenericBound<'tcx>| {
|
||||
if let GenericBound::Trait(t) = bound {
|
||||
Some((into_comparable_trait_ref(&t.trait_ref), t.span))
|
||||
Some((
|
||||
ComparableTraitRef {
|
||||
cx,
|
||||
trait_ref: &t.trait_ref,
|
||||
modifier: t.modifiers,
|
||||
},
|
||||
t.span,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let mut i = 0usize;
|
||||
for bound in bounds.iter().filter_map(only_comparable_trait_refs) {
|
||||
let (comparable_bound, span_direct) = bound;
|
||||
match map.entry(comparable_bound) {
|
||||
IndexEntry::Occupied(_) => repeated_res = true,
|
||||
IndexEntry::Vacant(e) => {
|
||||
e.insert((span_direct, i));
|
||||
i += 1;
|
||||
e.insert(span_direct);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Put bounds in source order
|
||||
let mut comparable_bounds = vec![Default::default(); map.len()];
|
||||
for (k, (v, i)) in map {
|
||||
comparable_bounds[i] = (k, v);
|
||||
}
|
||||
let comparable_bounds: Vec<_> = map.into_iter().collect();
|
||||
|
||||
if repeated_res && let [first_trait, .., last_trait] = bounds {
|
||||
let all_trait_span = first_trait.span().to(last_trait.span());
|
||||
|
|
|
@ -25,14 +25,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
|||
return;
|
||||
}
|
||||
|
||||
let args: Vec<_> = match expr.kind {
|
||||
ExprKind::Call(_, args) => args.iter().collect(),
|
||||
ExprKind::MethodCall(_, receiver, args, _) => std::iter::once(receiver).chain(args.iter()).collect(),
|
||||
let (reciever, args) = match expr.kind {
|
||||
ExprKind::Call(_, args) => (None, args),
|
||||
ExprKind::MethodCall(_, receiver, args, _) => (Some(receiver), args),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let args_to_recover = args
|
||||
let args_to_recover = reciever
|
||||
.into_iter()
|
||||
.chain(args)
|
||||
.filter(|arg| {
|
||||
if cx.typeck_results().expr_ty(arg).is_unit() && !utils::is_unit_literal(arg) {
|
||||
!matches!(
|
||||
|
|
158
clippy_lints/src/unnecessary_literal_bound.rs
Normal file
158
clippy_lints/src/unnecessary_literal_bound.rs
Normal file
|
@ -0,0 +1,158 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::path_res;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{FnKind, Visitor};
|
||||
use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnRetTy, Lit, MutTy, Mutability, PrimTy, Ty, TyKind, intravisit};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Detects functions that are written to return `&str` that could return `&'static str` but instead return a `&'a str`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// This leaves the caller unable to use the `&str` as `&'static str`, causing unneccessary allocations or confusion.
|
||||
/// This is also most likely what you meant to write.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # struct MyType;
|
||||
/// impl MyType {
|
||||
/// fn returns_literal(&self) -> &str {
|
||||
/// "Literal"
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # struct MyType;
|
||||
/// impl MyType {
|
||||
/// fn returns_literal(&self) -> &'static str {
|
||||
/// "Literal"
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Or, in case you may return a non-literal `str` in future:
|
||||
/// ```no_run
|
||||
/// # struct MyType;
|
||||
/// impl MyType {
|
||||
/// fn returns_literal<'a>(&'a self) -> &'a str {
|
||||
/// "Literal"
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.83.0"]
|
||||
pub UNNECESSARY_LITERAL_BOUND,
|
||||
pedantic,
|
||||
"detects &str that could be &'static str in function return types"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UnnecessaryLiteralBound => [UNNECESSARY_LITERAL_BOUND]);
|
||||
|
||||
fn extract_anonymous_ref<'tcx>(hir_ty: &Ty<'tcx>) -> Option<&'tcx Ty<'tcx>> {
|
||||
let TyKind::Ref(lifetime, MutTy { ty, mutbl }) = hir_ty.kind else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if !lifetime.is_anonymous() || !matches!(mutbl, Mutability::Not) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(ty)
|
||||
}
|
||||
|
||||
fn is_str_literal(expr: &Expr<'_>) -> bool {
|
||||
matches!(
|
||||
expr.kind,
|
||||
ExprKind::Lit(Lit {
|
||||
node: LitKind::Str(..),
|
||||
..
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
struct FindNonLiteralReturn;
|
||||
|
||||
impl<'hir> Visitor<'hir> for FindNonLiteralReturn {
|
||||
type Result = std::ops::ControlFlow<()>;
|
||||
type NestedFilter = intravisit::nested_filter::None;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'hir Expr<'hir>) -> Self::Result {
|
||||
if let ExprKind::Ret(Some(ret_val_expr)) = expr.kind
|
||||
&& !is_str_literal(ret_val_expr)
|
||||
{
|
||||
Self::Result::Break(())
|
||||
} else {
|
||||
intravisit::walk_expr(self, expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_implicit_returns_static_str(body: &Body<'_>) -> bool {
|
||||
// TODO: Improve this to the same complexity as the Visitor to catch more implicit return cases.
|
||||
if let ExprKind::Block(block, _) = body.value.kind
|
||||
&& let Some(implicit_ret) = block.expr
|
||||
{
|
||||
return is_str_literal(implicit_ret);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn check_explicit_returns_static_str(expr: &Expr<'_>) -> bool {
|
||||
let mut visitor = FindNonLiteralReturn;
|
||||
visitor.visit_expr(expr).is_continue()
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UnnecessaryLiteralBound {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'tcx>,
|
||||
kind: FnKind<'tcx>,
|
||||
decl: &'tcx FnDecl<'_>,
|
||||
body: &'tcx Body<'_>,
|
||||
span: Span,
|
||||
_: LocalDefId,
|
||||
) {
|
||||
if span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Checking closures would be a little silly
|
||||
if matches!(kind, FnKind::Closure) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for `-> &str`
|
||||
let FnRetTy::Return(ret_hir_ty) = decl.output else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(inner_hir_ty) = extract_anonymous_ref(ret_hir_ty) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if path_res(cx, inner_hir_ty) != Res::PrimTy(PrimTy::Str) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for all return statements returning literals
|
||||
if check_explicit_returns_static_str(body.value) && check_implicit_returns_static_str(body) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_LITERAL_BOUND,
|
||||
ret_hir_ty.span,
|
||||
"returning a `str` unnecessarily tied to the lifetime of arguments",
|
||||
"try",
|
||||
"&'static str".into(), // how ironic, a lint about `&'static str` requiring a `String` alloc...
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,13 +38,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
|
|||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
if let hir::ExprKind::MethodCall(path, recv, args, ..) = expr.kind
|
||||
if let hir::ExprKind::MethodCall(path, recv, [map_arg], ..) = expr.kind
|
||||
&& let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv))
|
||||
{
|
||||
let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, constructor_args) =
|
||||
recv.kind
|
||||
let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, [arg, ..]) = recv.kind
|
||||
&& let hir::ExprKind::Path(constructor_path) = constructor.kind
|
||||
&& let Some(arg) = constructor_args.first()
|
||||
{
|
||||
if constructor.span.from_expansion() || arg.span.from_expansion() {
|
||||
return;
|
||||
|
@ -70,9 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
|
|||
_ => return,
|
||||
}
|
||||
|
||||
if let Some(map_arg) = args.first()
|
||||
&& let hir::ExprKind::Path(fun) = map_arg.kind
|
||||
{
|
||||
if let hir::ExprKind::Path(fun) = map_arg.kind {
|
||||
if map_arg.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -52,8 +52,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
|
|||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if cx.tcx.is_diagnostic_item(sym::from_fn, fun_def_id)
|
||||
&& let [.., last_arg] = args
|
||||
&& let ExprKind::Lit(spanned) = &last_arg.kind
|
||||
&& let [arg] = args
|
||||
&& let ExprKind::Lit(spanned) = &arg.kind
|
||||
&& let LitKind::Str(symbol, _) = spanned.node
|
||||
&& symbol.is_empty()
|
||||
&& let inner_expr_type = cx.typeck_results().expr_ty(inner_expr)
|
||||
|
|
|
@ -222,7 +222,7 @@ fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
|
|||
}
|
||||
|
||||
fn unpack_try<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
|
||||
while let ExprKind::Call(func, [ref arg_0, ..]) = expr.kind
|
||||
while let ExprKind::Call(func, [ref arg_0]) = expr.kind
|
||||
&& matches!(
|
||||
func.kind,
|
||||
ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..))
|
||||
|
@ -244,7 +244,7 @@ fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
|
|||
/// waited on. Otherwise return None.
|
||||
fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
|
||||
if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind {
|
||||
if let ExprKind::Call(func, [ref arg_0, ..]) = expr.kind {
|
||||
if let ExprKind::Call(func, [ref arg_0]) = expr.kind {
|
||||
if matches!(
|
||||
func.kind,
|
||||
ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..))
|
||||
|
|
|
@ -26,7 +26,7 @@ declare_clippy_lint! {
|
|||
/// # fn some_function() -> Result<(), ()> { Ok(()) }
|
||||
/// let _ = some_function();
|
||||
/// ```
|
||||
#[clippy::version = "1.70.0"]
|
||||
#[clippy::version = "1.82.0"]
|
||||
pub UNUSED_RESULT_OK,
|
||||
restriction,
|
||||
"Use of `.ok()` to silence `Result`'s `#[must_use]` is misleading. Use `let _ =` instead."
|
||||
|
|
|
@ -153,13 +153,12 @@ fn collect_unwrap_info<'tcx>(
|
|||
}
|
||||
} else if let ExprKind::Unary(UnOp::Not, expr) = &expr.kind {
|
||||
return collect_unwrap_info(cx, if_expr, expr, branch, !invert, false);
|
||||
} else if let ExprKind::MethodCall(method_name, receiver, args, _) = &expr.kind
|
||||
} else if let ExprKind::MethodCall(method_name, receiver, [], _) = &expr.kind
|
||||
&& let Some(local_id) = path_to_local(receiver)
|
||||
&& let ty = cx.typeck_results().expr_ty(receiver)
|
||||
&& let name = method_name.ident.as_str()
|
||||
&& (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name))
|
||||
{
|
||||
assert!(args.is_empty());
|
||||
let unwrappable = match name {
|
||||
"is_some" | "is_ok" => true,
|
||||
"is_err" | "is_none" => false,
|
||||
|
@ -208,7 +207,7 @@ struct MutationVisitor<'tcx> {
|
|||
/// expression: that will be where the actual method call is.
|
||||
fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool {
|
||||
if let Node::Expr(mutating_expr) = tcx.parent_hir_node(expr_id)
|
||||
&& let ExprKind::MethodCall(path, ..) = mutating_expr.kind
|
||||
&& let ExprKind::MethodCall(path, _, [], _) = mutating_expr.kind
|
||||
{
|
||||
path.ident.name.as_str() == "as_mut"
|
||||
} else {
|
||||
|
@ -275,7 +274,7 @@ enum AsRefKind {
|
|||
/// Checks if the expression is a method call to `as_{ref,mut}` and returns the receiver of it.
|
||||
/// If it isn't, the expression itself is returned.
|
||||
fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Option<AsRefKind>) {
|
||||
if let ExprKind::MethodCall(path, recv, ..) = expr.kind {
|
||||
if let ExprKind::MethodCall(path, recv, [], _) = expr.kind {
|
||||
if path.ident.name == sym::as_ref {
|
||||
(recv, Some(AsRefKind::AsRef))
|
||||
} else if path.ident.name.as_str() == "as_mut" {
|
||||
|
@ -303,7 +302,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> {
|
|||
self.visit_branch(expr, cond, else_inner, true);
|
||||
}
|
||||
} else {
|
||||
// find `unwrap[_err]()` calls:
|
||||
// find `unwrap[_err]()` or `expect("...")` calls:
|
||||
if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind
|
||||
&& let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg)
|
||||
&& let Some(id) = path_to_local(self_arg)
|
||||
|
|
|
@ -129,7 +129,7 @@ fn into_iter_bound<'tcx>(
|
|||
|
||||
/// Extracts the receiver of a `.into_iter()` method call.
|
||||
fn into_iter_call<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Option<&'hir Expr<'hir>> {
|
||||
if let ExprKind::MethodCall(name, recv, _, _) = expr.kind
|
||||
if let ExprKind::MethodCall(name, recv, [], _) = expr.kind
|
||||
&& is_trait_method(cx, expr, sym::IntoIterator)
|
||||
&& name.ident.name == sym::into_iter
|
||||
{
|
||||
|
@ -173,7 +173,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||
}
|
||||
},
|
||||
|
||||
ExprKind::MethodCall(name, recv, ..) => {
|
||||
ExprKind::MethodCall(name, recv, [], _) => {
|
||||
if is_trait_method(cx, e, sym::Into) && name.ident.as_str() == "into" {
|
||||
let a = cx.typeck_results().expr_ty(e);
|
||||
let b = cx.typeck_results().expr_ty(recv);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue