1
Fork 0

Merge remote-tracking branch 'upstream/master' into rustup

This commit is contained in:
Philipp Krones 2024-02-22 15:59:29 +01:00
commit dc0bb69e66
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
1053 changed files with 12006 additions and 10203 deletions

4
.github/driver.sh vendored
View file

@ -32,7 +32,7 @@ test "$sysroot" = $desired_sysroot
) )
# Check that the --sysroot argument is only passed once via arg_file.txt (SYSROOT is ignored) # Check that the --sysroot argument is only passed once via arg_file.txt (SYSROOT is ignored)
( (
echo "fn main() {}" > target/driver_test.rs echo "fn main() {}" > target/driver_test.rs
echo "--sysroot="$(./target/debug/clippy-driver --print sysroot)"" > arg_file.txt echo "--sysroot="$(./target/debug/clippy-driver --print sysroot)"" > arg_file.txt
echo "--verbose" >> arg_file.txt echo "--verbose" >> arg_file.txt
@ -45,7 +45,7 @@ unset CARGO_MANIFEST_DIR
# Run a lint and make sure it produces the expected output. It's also expected to exit with code 1 # Run a lint and make sure it produces the expected output. It's also expected to exit with code 1
# FIXME: How to match the clippy invocation in compile-test.rs? # FIXME: How to match the clippy invocation in compile-test.rs?
./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2>double_neg.stderr && exit 1 ./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2>double_neg.stderr && exit 1
sed -e "s,tests/ui,\$DIR," -e "/= help: for/d" double_neg.stderr > normalized.stderr sed -e "/= help: for/d" double_neg.stderr > normalized.stderr
diff -u normalized.stderr tests/ui/double_neg.stderr diff -u normalized.stderr tests/ui/double_neg.stderr
# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same

View file

@ -5125,6 +5125,7 @@ Released 2018-09-13
[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access [`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
[`default_union_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_union_representation [`default_union_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_union_representation
[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr [`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
[`deprecated_clippy_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_clippy_cfg_attr
[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
[`deref_by_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_by_slicing [`deref_by_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_by_slicing
@ -5725,6 +5726,7 @@ Released 2018-09-13
[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints [`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
[`unnecessary_box_returns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns [`unnecessary_box_returns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns
[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
[`unnecessary_clippy_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_clippy_cfg
[`unnecessary_fallible_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fallible_conversions [`unnecessary_fallible_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fallible_conversions
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map [`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map

View file

@ -27,10 +27,10 @@ rustc_tools_util = "0.3.0"
tempfile = { version = "3.2", optional = true } tempfile = { version = "3.2", optional = true }
termize = "0.1" termize = "0.1"
color-print = "0.3.4" color-print = "0.3.4"
anstream = "0.5.0" anstream = "0.6.0"
[dev-dependencies] [dev-dependencies]
ui_test = "0.21.2" ui_test = "0.22.1"
tester = "0.9" tester = "0.9"
regex = "1.5" regex = "1.5"
toml = "0.7.3" toml = "0.7.3"

View file

@ -113,17 +113,14 @@ found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
Very rarely, you may wish to prevent Clippy from evaluating certain sections of code entirely. You can do this with Very rarely, you may wish to prevent Clippy from evaluating certain sections of code entirely. You can do this with
[conditional compilation](https://doc.rust-lang.org/reference/conditional-compilation.html) by checking that the [conditional compilation](https://doc.rust-lang.org/reference/conditional-compilation.html) by checking that the
`cargo-clippy` feature is not set. You may need to provide a stub so that the code compiles: `clippy` cfg is not set. You may need to provide a stub so that the code compiles:
```rust ```rust
#[cfg(not(feature = "cargo-clippy"))] #[cfg(not(clippy)]
include!(concat!(env!("OUT_DIR"), "/my_big_function-generated.rs")); include!(concat!(env!("OUT_DIR"), "/my_big_function-generated.rs"));
#[cfg(feature = "cargo-clippy")] #[cfg(clippy)]
fn my_big_function(_input: &str) -> Option<MyStruct> { fn my_big_function(_input: &str) -> Option<MyStruct> {
None None
} }
``` ```
This feature is not actually part of your crate, so specifying `--all-features` to other tools, e.g. `cargo test
--all-features`, will not disable it.

View file

@ -82,7 +82,7 @@ The output looks something like this (from the example earlier):
```text ```text
error: an inclusive range would be more readable error: an inclusive range would be more readable
--> $DIR/range_plus_minus_one.rs:37:14 --> tests/ui/range_plus_minus_one.rs:37:14
| |
LL | for _ in 1..1 + 1 {} LL | for _ in 1..1 + 1 {}
| ^^^^^^^^ help: use: `1..=1` | ^^^^^^^^ help: use: `1..=1`
@ -135,14 +135,14 @@ Examples:
```text ```text
error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
--> $DIR/drop_forget_ref.rs:10:5 --> tests/ui/drop_forget_ref.rs:10:5
| |
10 | forget(&SomeStruct); 10 | forget(&SomeStruct);
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
| |
= note: `-D clippy::forget-ref` implied by `-D warnings` = note: `-D clippy::forget-ref` implied by `-D warnings`
note: argument has type &SomeStruct note: argument has type &SomeStruct
--> $DIR/drop_forget_ref.rs:10:12 --> tests/ui/drop_forget_ref.rs:10:12
| |
10 | forget(&SomeStruct); 10 | forget(&SomeStruct);
| ^^^^^^^^^^^ | ^^^^^^^^^^^
@ -158,7 +158,7 @@ Example:
```text ```text
error: constant division of 0.0 with 0.0 will always result in NaN error: constant division of 0.0 with 0.0 will always result in NaN
--> $DIR/zero_div_zero.rs:6:25 --> tests/ui/zero_div_zero.rs:6:25
| |
6 | let other_f64_nan = 0.0f64 / 0.0; 6 | let other_f64_nan = 0.0f64 / 0.0;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
@ -176,7 +176,7 @@ Example:
```text ```text
error: This `.fold` can be more succinctly expressed as `.any` error: This `.fold` can be more succinctly expressed as `.any`
--> $DIR/methods.rs:390:13 --> tests/ui/methods.rs:390:13
| |
390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2); 390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`

View file

@ -97,19 +97,19 @@ failures:
---- compile_test stdout ---- ---- compile_test stdout ----
normalized stderr: normalized stderr:
error: function called "foo" error: function called "foo"
--> $DIR/foo_functions.rs:6:12 --> tests/ui/foo_functions.rs:6:12
| |
LL | pub fn foo(&self) {} LL | pub fn foo(&self) {}
| ^^^ | ^^^
| |
= note: `-D clippy::foo-functions` implied by `-D warnings` = note: `-D clippy::foo-functions` implied by `-D warnings`
error: function called "foo" error: function called "foo"
--> $DIR/foo_functions.rs:13:8 --> tests/ui/foo_functions.rs:13:8
| |
LL | fn foo(&self) {} LL | fn foo(&self) {}
| ^^^ | ^^^
error: function called "foo" error: function called "foo"
--> $DIR/foo_functions.rs:19:4 --> tests/ui/foo_functions.rs:19:4
| |
LL | fn foo() {} LL | fn foo() {}
| ^^^ | ^^^

View file

@ -278,7 +278,7 @@ The minimum number of struct fields for the lints about field names to trigger
--- ---
**Affected lints:** **Affected lints:**
* [`struct_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#struct_variant_names) * [`struct_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#struct_field_names)
## `enum-variant-size-threshold` ## `enum-variant-size-threshold`

View file

@ -325,7 +325,7 @@ define_Conf! {
/// ///
/// The minimum number of enum variants for the lints about variant names to trigger /// The minimum number of enum variants for the lints about variant names to trigger
(enum_variant_name_threshold: u64 = 3), (enum_variant_name_threshold: u64 = 3),
/// Lint: STRUCT_VARIANT_NAMES. /// Lint: STRUCT_FIELD_NAMES.
/// ///
/// The minimum number of struct fields for the lints about field names to trigger /// The minimum number of struct fields for the lints about field names to trigger
(struct_field_name_threshold: u64 = 3), (struct_field_name_threshold: u64 = 3),
@ -648,7 +648,7 @@ fn deserialize(file: &SourceFile) -> TryConf {
extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS); extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES); extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
// TODO: THIS SHOULD BE TESTED, this comment will be gone soon // TODO: THIS SHOULD BE TESTED, this comment will be gone soon
if conf.conf.allowed_idents_below_min_chars.contains(&"..".to_owned()) { if conf.conf.allowed_idents_below_min_chars.contains("..") {
conf.conf conf.conf
.allowed_idents_below_min_chars .allowed_idents_below_min_chars
.extend(DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string)); .extend(DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string));

View file

@ -6,7 +6,7 @@
clippy::missing_panics_doc, clippy::missing_panics_doc,
rustc::diagnostic_outside_of_impl, rustc::diagnostic_outside_of_impl,
rustc::untranslatable_diagnostic, rustc::untranslatable_diagnostic,
rustc::untranslatable_diagnostic_trivial, rustc::untranslatable_diagnostic_trivial
)] )]
extern crate rustc_ast; extern crate rustc_ast;

View file

@ -2,8 +2,11 @@ use std::fmt;
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions}; use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions};
use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; use rustc_ast::{InlineAsm, Item, ItemKind};
use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::Span;
use rustc_target::asm::InlineAsmArch;
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
enum AsmStyle { enum AsmStyle {
@ -31,8 +34,14 @@ impl std::ops::Not for AsmStyle {
} }
} }
fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr, check_for: AsmStyle) { fn check_asm_syntax(
if let ExprKind::InlineAsm(ref inline_asm) = expr.kind { lint: &'static Lint,
cx: &EarlyContext<'_>,
inline_asm: &InlineAsm,
span: Span,
check_for: AsmStyle,
) {
if matches!(cx.sess().asm_arch, Some(InlineAsmArch::X86 | InlineAsmArch::X86_64)) {
let style = if inline_asm.options.contains(InlineAsmOptions::ATT_SYNTAX) { let style = if inline_asm.options.contains(InlineAsmOptions::ATT_SYNTAX) {
AsmStyle::Att AsmStyle::Att
} else { } else {
@ -43,7 +52,7 @@ fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr
span_lint_and_help( span_lint_and_help(
cx, cx,
lint, lint,
expr.span, span,
&format!("{style} x86 assembly syntax used"), &format!("{style} x86 assembly syntax used"),
None, None,
&format!("use {} x86 assembly syntax", !style), &format!("use {} x86 assembly syntax", !style),
@ -89,7 +98,15 @@ declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]);
impl EarlyLintPass for InlineAsmX86IntelSyntax { impl EarlyLintPass for InlineAsmX86IntelSyntax {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Intel); if let ExprKind::InlineAsm(inline_asm) = &expr.kind {
check_asm_syntax(Self::get_lints()[0], cx, inline_asm, expr.span, AsmStyle::Intel);
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
if let ItemKind::GlobalAsm(inline_asm) = &item.kind {
check_asm_syntax(Self::get_lints()[0], cx, inline_asm, item.span, AsmStyle::Intel);
}
} }
} }
@ -130,6 +147,14 @@ declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]);
impl EarlyLintPass for InlineAsmX86AttSyntax { impl EarlyLintPass for InlineAsmX86AttSyntax {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Att); if let ExprKind::InlineAsm(inline_asm) = &expr.kind {
check_asm_syntax(Self::get_lints()[0], cx, inline_asm, expr.span, AsmStyle::Att);
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
if let ItemKind::GlobalAsm(inline_asm) = &item.kind {
check_asm_syntax(Self::get_lints()[0], cx, inline_asm, item.span, AsmStyle::Att);
}
} }
} }

View file

@ -1,7 +1,9 @@
//! checks for attributes //! checks for attributes
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{
span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
};
use clippy_utils::is_from_proc_macro; use clippy_utils::is_from_proc_macro;
use clippy_utils::macros::{is_panic, macro_backtrace}; use clippy_utils::macros::{is_panic, macro_backtrace};
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
@ -433,6 +435,56 @@ declare_clippy_lint! {
"prevent from misusing the wrong attr name" "prevent from misusing the wrong attr name"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for
/// `#[cfg(feature = "cargo-clippy")]` and suggests to replace it with
/// `#[cfg_attr(clippy, ...)]` or `#[cfg(clippy)]`.
///
/// ### Why is this bad?
/// This feature has been deprecated for years and shouldn't be used anymore.
///
/// ### Example
/// ```no_run
/// #[cfg(feature = "cargo-clippy")]
/// struct Bar;
/// ```
///
/// Use instead:
/// ```no_run
/// #[cfg(clippy)]
/// struct Bar;
/// ```
#[clippy::version = "1.78.0"]
pub DEPRECATED_CLIPPY_CFG_ATTR,
suspicious,
"usage of `cfg(feature = \"cargo-clippy\")` instead of `cfg(clippy)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `#[cfg_attr(clippy, allow(clippy::lint))]`
/// and suggests to replace it with `#[allow(clippy::lint)]`.
///
/// ### Why is this bad?
/// There is no reason to put clippy attributes behind a clippy `cfg` as they are not
/// run by anything else than clippy.
///
/// ### Example
/// ```no_run
/// #![cfg_attr(clippy, allow(clippy::deprecated_cfg_attr))]
/// ```
///
/// Use instead:
/// ```no_run
/// #![allow(clippy::deprecated_cfg_attr)]
/// ```
#[clippy::version = "1.78.0"]
pub UNNECESSARY_CLIPPY_CFG,
suspicious,
"usage of `cfg_attr(clippy, allow(clippy::lint))` instead of `allow(clippy::lint)`"
}
declare_lint_pass!(Attributes => [ declare_lint_pass!(Attributes => [
ALLOW_ATTRIBUTES_WITHOUT_REASON, ALLOW_ATTRIBUTES_WITHOUT_REASON,
INLINE_ALWAYS, INLINE_ALWAYS,
@ -794,6 +846,8 @@ impl_lint_pass!(EarlyAttributes => [
EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_DOC_COMMENTS,
NON_MINIMAL_CFG, NON_MINIMAL_CFG,
MAYBE_MISUSED_CFG, MAYBE_MISUSED_CFG,
DEPRECATED_CLIPPY_CFG_ATTR,
UNNECESSARY_CLIPPY_CFG,
]); ]);
impl EarlyLintPass for EarlyAttributes { impl EarlyLintPass for EarlyAttributes {
@ -803,6 +857,7 @@ impl EarlyLintPass for EarlyAttributes {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
check_deprecated_cfg_attr(cx, attr, &self.msrv); check_deprecated_cfg_attr(cx, attr, &self.msrv);
check_deprecated_cfg(cx, attr);
check_mismatched_target_os(cx, attr); check_mismatched_target_os(cx, attr);
check_minimal_cfg_condition(cx, attr); check_minimal_cfg_condition(cx, attr);
check_misused_cfg(cx, attr); check_misused_cfg(cx, attr);
@ -857,42 +912,151 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It
} }
} }
fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) { fn check_cargo_clippy_attr(cx: &EarlyContext<'_>, item: &rustc_ast::MetaItem) {
if msrv.meets(msrvs::TOOL_ATTRIBUTES) if item.has_name(sym::feature) && item.value_str().is_some_and(|v| v.as_str() == "cargo-clippy") {
// check cfg_attr
&& attr.has_name(sym::cfg_attr)
&& let Some(items) = attr.meta_item_list()
&& items.len() == 2
// check for `rustfmt`
&& let Some(feature_item) = items[0].meta_item()
&& feature_item.has_name(sym::rustfmt)
// check for `rustfmt_skip` and `rustfmt::skip`
&& let Some(skip_item) = &items[1].meta_item()
&& (skip_item.has_name(sym!(rustfmt_skip))
|| skip_item
.path
.segments
.last()
.expect("empty path in attribute")
.ident
.name
== sym::skip)
// Only lint outer attributes, because custom inner attributes are unstable
// Tracking issue: https://github.com/rust-lang/rust/issues/54726
&& attr.style == AttrStyle::Outer
{
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR,
attr.span, item.span,
"`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes", "`feature = \"cargo-clippy\"` was replaced by `clippy`",
"use", "replace with",
"#[rustfmt::skip]".to_string(), "clippy".to_string(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
} }
fn check_deprecated_cfg_recursively(cx: &EarlyContext<'_>, attr: &rustc_ast::MetaItem) {
if let Some(ident) = attr.ident() {
if ["any", "all", "not"].contains(&ident.name.as_str()) {
let Some(list) = attr.meta_item_list() else { return };
for item in list.iter().filter_map(|item| item.meta_item()) {
check_deprecated_cfg_recursively(cx, item);
}
} else {
check_cargo_clippy_attr(cx, attr);
}
}
}
fn check_deprecated_cfg(cx: &EarlyContext<'_>, attr: &Attribute) {
if attr.has_name(sym::cfg)
&& let Some(list) = attr.meta_item_list()
{
for item in list.iter().filter_map(|item| item.meta_item()) {
check_deprecated_cfg_recursively(cx, item);
}
}
}
fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) {
// check cfg_attr
if attr.has_name(sym::cfg_attr)
&& let Some(items) = attr.meta_item_list()
&& items.len() == 2
&& let Some(feature_item) = items[0].meta_item()
{
// check for `rustfmt`
if feature_item.has_name(sym::rustfmt)
&& msrv.meets(msrvs::TOOL_ATTRIBUTES)
// check for `rustfmt_skip` and `rustfmt::skip`
&& let Some(skip_item) = &items[1].meta_item()
&& (skip_item.has_name(sym!(rustfmt_skip))
|| skip_item
.path
.segments
.last()
.expect("empty path in attribute")
.ident
.name
== sym::skip)
// Only lint outer attributes, because custom inner attributes are unstable
// Tracking issue: https://github.com/rust-lang/rust/issues/54726
&& attr.style == AttrStyle::Outer
{
span_lint_and_sugg(
cx,
DEPRECATED_CFG_ATTR,
attr.span,
"`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes",
"use",
"#[rustfmt::skip]".to_string(),
Applicability::MachineApplicable,
);
} else {
check_deprecated_cfg_recursively(cx, feature_item);
if let Some(behind_cfg_attr) = items[1].meta_item() {
check_clippy_cfg_attr(cx, feature_item, behind_cfg_attr, attr);
}
}
}
}
fn check_clippy_cfg_attr(
cx: &EarlyContext<'_>,
cfg_attr: &rustc_ast::MetaItem,
behind_cfg_attr: &rustc_ast::MetaItem,
attr: &Attribute,
) {
if cfg_attr.has_name(sym::clippy)
&& let Some(ident) = behind_cfg_attr.ident()
// FIXME: replace with `from_symbol` once https://github.com/rust-lang/rust/pull/121230
// is merged.
&& Level::from_str(ident.name.as_str()).is_some()
&& let Some(items) = behind_cfg_attr.meta_item_list()
{
let nb_items = items.len();
let mut clippy_lints = Vec::with_capacity(items.len());
for item in items {
if let Some(meta_item) = item.meta_item()
&& let [part1, _] = meta_item.path.segments.as_slice()
&& part1.ident.name == sym::clippy
{
clippy_lints.push(item.span());
}
}
if clippy_lints.is_empty() {
return;
}
if nb_items == clippy_lints.len() {
if let Some(snippet) = snippet_opt(cx, behind_cfg_attr.span) {
span_lint_and_sugg(
cx,
UNNECESSARY_CLIPPY_CFG,
attr.span,
"no need to put clippy lints behind a `clippy` cfg",
"replace with",
format!(
"#{}[{}]",
if attr.style == AttrStyle::Inner { "!" } else { "" },
snippet
),
Applicability::MachineApplicable,
);
}
} else {
let snippet = clippy_lints
.iter()
.filter_map(|sp| snippet_opt(cx, *sp))
.collect::<Vec<_>>()
.join(",");
span_lint_and_note(
cx,
UNNECESSARY_CLIPPY_CFG,
clippy_lints,
"no need to put clippy lints behind a `clippy` cfg",
None,
&format!(
"write instead: `#{}[{}({})]`",
if attr.style == AttrStyle::Inner { "!" } else { "" },
ident.name,
snippet
),
);
}
}
}
fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) { fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
for item in items { for item in items {
if let NestedMetaItem::MetaItem(meta) = item { if let NestedMetaItem::MetaItem(meta) = item {

View file

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::source::snippet_block_with_applicability; use clippy_utils::source::snippet_block_with_applicability;
use clippy_utils::ty::implements_trait; use clippy_utils::ty::implements_trait;
use clippy_utils::visitors::{for_each_expr, Descend}; use clippy_utils::visitors::{for_each_expr, Descend};
use clippy_utils::{get_parent_expr, higher}; use clippy_utils::{get_parent_expr, higher, is_from_proc_macro};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource};
@ -13,7 +13,7 @@ use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for `if` conditions that use blocks containing an /// Checks for `if` and `match` conditions that use blocks containing an
/// expression, statements or conditions that use closures with blocks. /// expression, statements or conditions that use closures with blocks.
/// ///
/// ### Why is this bad? /// ### Why is this bad?
@ -25,6 +25,11 @@ declare_clippy_lint! {
/// if { true } { /* ... */ } /// if { true } { /* ... */ }
/// ///
/// if { let x = somefunc(); x } { /* ... */ } /// if { let x = somefunc(); x } { /* ... */ }
///
/// match { let e = somefunc(); e } {
/// // ...
/// # _ => {}
/// }
/// ``` /// ```
/// ///
/// Use instead: /// Use instead:
@ -34,6 +39,12 @@ declare_clippy_lint! {
/// ///
/// let res = { let x = somefunc(); x }; /// let res = { let x = somefunc(); x };
/// if res { /* ... */ } /// if res { /* ... */ }
///
/// let res = { let e = somefunc(); e };
/// match res {
/// // ...
/// # _ => {}
/// }
/// ``` /// ```
#[clippy::version = "1.45.0"] #[clippy::version = "1.45.0"]
pub BLOCKS_IN_CONDITIONS, pub BLOCKS_IN_CONDITIONS,
@ -94,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions {
} }
} else { } else {
let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span); let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
if span.from_expansion() || expr.span.from_expansion() { if span.from_expansion() || expr.span.from_expansion() || is_from_proc_macro(cx, cond) {
return; return;
} }
// move block higher // move block higher

View file

@ -85,7 +85,117 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
) { ) {
NonminimalBoolVisitor { cx }.visit_body(body); NonminimalBoolVisitor { cx }.visit_body(body);
} }
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
match expr.kind {
ExprKind::Unary(UnOp::Not, sub) => check_inverted_condition(cx, expr.span, sub),
// This check the case where an element in a boolean comparison is inverted, like:
//
// ```
// let a = true;
// !a == false;
// ```
ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) => {
check_inverted_bool_in_condition(cx, expr.span, op.node, left, right);
},
_ => {},
}
}
} }
fn inverted_bin_op_eq_str(op: BinOpKind) -> Option<&'static str> {
match op {
BinOpKind::Eq => Some("!="),
BinOpKind::Ne => Some("=="),
_ => None,
}
}
fn bin_op_eq_str(op: BinOpKind) -> Option<&'static str> {
match op {
BinOpKind::Eq => Some("=="),
BinOpKind::Ne => Some("!="),
_ => None,
}
}
fn check_inverted_condition(cx: &LateContext<'_>, expr_span: Span, sub_expr: &Expr<'_>) {
if !expr_span.from_expansion()
&& let ExprKind::Binary(op, left, right) = sub_expr.kind
&& let Some(left) = snippet_opt(cx, left.span)
&& let Some(right) = snippet_opt(cx, right.span)
{
let Some(op) = inverted_bin_op_eq_str(op.node) else {
return;
};
span_lint_and_sugg(
cx,
NONMINIMAL_BOOL,
expr_span,
"this boolean expression can be simplified",
"try",
format!("{left} {op} {right}",),
Applicability::MachineApplicable,
);
}
}
fn check_inverted_bool_in_condition(
cx: &LateContext<'_>,
expr_span: Span,
op: BinOpKind,
left: &Expr<'_>,
right: &Expr<'_>,
) {
if expr_span.from_expansion()
&& (!cx.typeck_results().node_types()[left.hir_id].is_bool()
|| !cx.typeck_results().node_types()[right.hir_id].is_bool())
{
return;
}
let suggestion = match (left.kind, right.kind) {
(ExprKind::Unary(UnOp::Not, left_sub), ExprKind::Unary(UnOp::Not, right_sub)) => {
let Some(left) = snippet_opt(cx, left_sub.span) else {
return;
};
let Some(right) = snippet_opt(cx, right_sub.span) else {
return;
};
let Some(op) = bin_op_eq_str(op) else { return };
format!("{left} {op} {right}")
},
(ExprKind::Unary(UnOp::Not, left_sub), _) => {
let Some(left) = snippet_opt(cx, left_sub.span) else {
return;
};
let Some(right) = snippet_opt(cx, right.span) else {
return;
};
let Some(op) = inverted_bin_op_eq_str(op) else { return };
format!("{left} {op} {right}")
},
(_, ExprKind::Unary(UnOp::Not, right_sub)) => {
let Some(left) = snippet_opt(cx, left.span) else { return };
let Some(right) = snippet_opt(cx, right_sub.span) else {
return;
};
let Some(op) = inverted_bin_op_eq_str(op) else { return };
format!("{left} {op} {right}")
},
_ => return,
};
span_lint_and_sugg(
cx,
NONMINIMAL_BOOL,
expr_span,
"this boolean expression can be simplified",
"try",
suggestion,
Applicability::MachineApplicable,
);
}
struct NonminimalBoolVisitor<'a, 'tcx> { struct NonminimalBoolVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
} }

View file

@ -101,25 +101,22 @@ impl<'tcx> Visitor<'tcx> for InferVisitor {
fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match get_parent_node(cx.tcx, expr.hir_id) { match get_parent_node(cx.tcx, expr.hir_id) {
Some(Node::Local(Local { ty: Some(ty), .. })) => { Node::Local(Local { ty: Some(ty), .. }) => {
let mut v = InferVisitor::default(); let mut v = InferVisitor::default();
v.visit_ty(ty); v.visit_ty(ty);
!v.0 !v.0
}, },
Some( Node::Expr(Expr {
Node::Expr(Expr { kind: ExprKind::Call(path, args),
..
})
| Node::Block(Block {
expr: Some(Expr {
kind: ExprKind::Call(path, args), kind: ExprKind::Call(path, args),
.. ..
})
| Node::Block(Block {
expr:
Some(Expr {
kind: ExprKind::Call(path, args),
..
}),
..
}), }),
) => { ..
}) => {
if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id) if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
&& let Some(sig) = expr_sig(cx, path) && let Some(sig) = expr_sig(cx, path)
&& let Some(input) = sig.input(index) && let Some(input) = sig.input(index)

View file

@ -131,8 +131,7 @@ pub(super) fn check(
let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),)); let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),));
let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) { let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
(false, false) if from_nbits > to_nbits => "", (_, false) if from_nbits > to_nbits => "",
(true, false) if from_nbits > to_nbits => "",
(false, true) if from_nbits > 64 => "", (false, true) if from_nbits > 64 => "",
(false, true) if from_nbits > 32 => " on targets with 32-bit wide pointers", (false, true) if from_nbits > 32 => " on targets with 32-bit wide pointers",
_ => return, _ => return,

View file

@ -264,8 +264,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
} }
// Local usage // Local usage
} else if let Res::Local(hir_id) = res } else if let Res::Local(hir_id) = res
&& let Some(parent) = get_parent_node(cx.tcx, hir_id) && let Node::Local(l) = get_parent_node(cx.tcx, hir_id)
&& let Node::Local(l) = parent
{ {
if let Some(e) = l.init if let Some(e) = l.init
&& is_cast_from_ty_alias(cx, e, cast_from) && is_cast_from_ty_alias(cx, e, cast_from)

View file

@ -94,7 +94,7 @@ fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Bloc
// `id` appearing in the left-hand side of an assignment is not a read access: // `id` appearing in the left-hand side of an assignment is not a read access:
// //
// id = ...; // Not reading `id`. // id = ...; // Not reading `id`.
if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id) if let Node::Expr(parent) = get_parent_node(cx.tcx, expr.hir_id)
&& let ExprKind::Assign(lhs, ..) = parent.kind && let ExprKind::Assign(lhs, ..) = parent.kind
&& path_to_local_id(lhs, id) && path_to_local_id(lhs, id)
{ {
@ -108,7 +108,7 @@ fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Bloc
// Only assuming this for "official" methods defined on the type. For methods defined in extension // Only assuming this for "official" methods defined on the type. For methods defined in extension
// traits (identified as local, based on the orphan rule), pessimistically assume that they might // traits (identified as local, based on the orphan rule), pessimistically assume that they might
// have side effects, so consider them a read. // have side effects, so consider them a read.
if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id) if let Node::Expr(parent) = get_parent_node(cx.tcx, expr.hir_id)
&& let ExprKind::MethodCall(_, receiver, _, _) = parent.kind && let ExprKind::MethodCall(_, receiver, _, _) = parent.kind
&& path_to_local_id(receiver, id) && path_to_local_id(receiver, id)
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
@ -117,7 +117,7 @@ fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Bloc
// The method call is a statement, so the return value is not used. That's not a read access: // The method call is a statement, so the return value is not used. That's not a read access:
// //
// id.foo(args); // id.foo(args);
if let Some(Node::Stmt(..)) = get_parent_node(cx.tcx, parent.hir_id) { if let Node::Stmt(..) = get_parent_node(cx.tcx, parent.hir_id) {
return ControlFlow::Continue(()); return ControlFlow::Continue(());
} }

View file

@ -51,6 +51,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON_INFO, crate::attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON_INFO,
crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO, crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO,
crate::attrs::DEPRECATED_CFG_ATTR_INFO, crate::attrs::DEPRECATED_CFG_ATTR_INFO,
crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO,
crate::attrs::DEPRECATED_SEMVER_INFO, crate::attrs::DEPRECATED_SEMVER_INFO,
crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
@ -59,6 +60,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::attrs::MISMATCHED_TARGET_OS_INFO, crate::attrs::MISMATCHED_TARGET_OS_INFO,
crate::attrs::NON_MINIMAL_CFG_INFO, crate::attrs::NON_MINIMAL_CFG_INFO,
crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO, crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO,
crate::attrs::UNNECESSARY_CLIPPY_CFG_INFO,
crate::attrs::USELESS_ATTRIBUTE_INFO, crate::attrs::USELESS_ATTRIBUTE_INFO,
crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO, crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO,
crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO, crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO,

View file

@ -1009,7 +1009,7 @@ fn report<'tcx>(
state.msg, state.msg,
|diag| { |diag| {
let (precedence, calls_field) = match get_parent_node(cx.tcx, data.first_expr.hir_id) { let (precedence, calls_field) = match get_parent_node(cx.tcx, data.first_expr.hir_id) {
Some(Node::Expr(e)) => match e.kind { Node::Expr(e) => match e.kind {
ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false), ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false),
ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))), ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))),
_ => (e.precedence().order(), false), _ => (e.precedence().order(), false),

View file

@ -1,13 +1,16 @@
use clippy_config::types::DisallowedPath; use clippy_config::types::DisallowedPath;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::macros::macro_backtrace; use clippy_utils::macros::macro_backtrace;
use rustc_ast::Attribute; use rustc_ast::Attribute;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::DiagnosticBuilder;
use rustc_hir::def_id::DefIdMap; use rustc_hir::def_id::DefIdMap;
use rustc_hir::{Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; use rustc_hir::{
Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty,
};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::{ExpnId, Span}; use rustc_span::{ExpnId, MacroKind, Span};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -57,6 +60,10 @@ pub struct DisallowedMacros {
conf_disallowed: Vec<DisallowedPath>, conf_disallowed: Vec<DisallowedPath>,
disallowed: DefIdMap<usize>, disallowed: DefIdMap<usize>,
seen: FxHashSet<ExpnId>, seen: FxHashSet<ExpnId>,
// Track the most recently seen node that can have a `derive` attribute.
// Needed to use the correct lint level.
derive_src: Option<OwnerId>,
} }
impl DisallowedMacros { impl DisallowedMacros {
@ -65,10 +72,11 @@ impl DisallowedMacros {
conf_disallowed, conf_disallowed,
disallowed: DefIdMap::default(), disallowed: DefIdMap::default(),
seen: FxHashSet::default(), seen: FxHashSet::default(),
derive_src: None,
} }
} }
fn check(&mut self, cx: &LateContext<'_>, span: Span) { fn check(&mut self, cx: &LateContext<'_>, span: Span, derive_src: Option<OwnerId>) {
if self.conf_disallowed.is_empty() { if self.conf_disallowed.is_empty() {
return; return;
} }
@ -80,18 +88,26 @@ impl DisallowedMacros {
if let Some(&index) = self.disallowed.get(&mac.def_id) { if let Some(&index) = self.disallowed.get(&mac.def_id) {
let conf = &self.conf_disallowed[index]; let conf = &self.conf_disallowed[index];
let msg = format!("use of a disallowed macro `{}`", conf.path());
span_lint_and_then( let add_note = |diag: &mut DiagnosticBuilder<'_, _>| {
cx, if let Some(reason) = conf.reason() {
DISALLOWED_MACROS, diag.note(reason);
mac.span, }
&format!("use of a disallowed macro `{}`", conf.path()), };
|diag| { if matches!(mac.kind, MacroKind::Derive)
if let Some(reason) = conf.reason() { && let Some(derive_src) = derive_src
diag.note(reason); {
} span_lint_hir_and_then(
}, cx,
); DISALLOWED_MACROS,
cx.tcx.local_def_id_to_hir_id(derive_src.def_id),
mac.span,
&msg,
add_note,
);
} else {
span_lint_and_then(cx, DISALLOWED_MACROS, mac.span, &msg, add_note);
}
} }
} }
} }
@ -110,49 +126,57 @@ impl LateLintPass<'_> for DisallowedMacros {
} }
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
self.check(cx, expr.span); self.check(cx, expr.span, None);
// `$t + $t` can have the context of $t, check also the span of the binary operator // `$t + $t` can have the context of $t, check also the span of the binary operator
if let ExprKind::Binary(op, ..) = expr.kind { if let ExprKind::Binary(op, ..) = expr.kind {
self.check(cx, op.span); self.check(cx, op.span, None);
} }
} }
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
self.check(cx, stmt.span); self.check(cx, stmt.span, None);
} }
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &Ty<'_>) { fn check_ty(&mut self, cx: &LateContext<'_>, ty: &Ty<'_>) {
self.check(cx, ty.span); self.check(cx, ty.span, None);
} }
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
self.check(cx, pat.span); self.check(cx, pat.span, None);
} }
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
self.check(cx, item.span); self.check(cx, item.span, self.derive_src);
self.check(cx, item.vis_span); self.check(cx, item.vis_span, None);
if matches!(
item.kind,
ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..)
) && macro_backtrace(item.span).all(|m| !matches!(m.kind, MacroKind::Derive))
{
self.derive_src = Some(item.owner_id);
}
} }
fn check_foreign_item(&mut self, cx: &LateContext<'_>, item: &ForeignItem<'_>) { fn check_foreign_item(&mut self, cx: &LateContext<'_>, item: &ForeignItem<'_>) {
self.check(cx, item.span); self.check(cx, item.span, None);
self.check(cx, item.vis_span); self.check(cx, item.vis_span, None);
} }
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) { fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) {
self.check(cx, item.span); self.check(cx, item.span, None);
self.check(cx, item.vis_span); self.check(cx, item.vis_span, None);
} }
fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
self.check(cx, item.span); self.check(cx, item.span, None);
} }
fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) { fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) {
self.check(cx, path.span); self.check(cx, path.span, None);
} }
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) { fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) {
self.check(cx, attr.span); self.check(cx, attr.span, self.derive_src);
} }
} }

View file

@ -144,8 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
// } // }
fn is_single_call_in_arm<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'_>, drop_expr: &'tcx Expr<'_>) -> bool { fn is_single_call_in_arm<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'_>, drop_expr: &'tcx Expr<'_>) -> bool {
if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) { if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) {
let parent_node = get_parent_node(cx.tcx, drop_expr.hir_id); if let Node::Arm(Arm { body, .. }) = get_parent_node(cx.tcx, drop_expr.hir_id) {
if let Some(Node::Arm(Arm { body, .. })) = &parent_node {
return body.hir_id == drop_expr.hir_id; return body.hir_id == drop_expr.hir_id;
} }
} }

View file

@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_diag_trait_item; use clippy_utils::is_diag_trait_item;
use clippy_utils::macros::{ use clippy_utils::macros::{
find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro,
is_format_macro, is_panic, root_macro_call, root_macro_call_first_node, FormatParamUsage, is_format_macro, is_panic, root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall,
}; };
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{implements_trait, is_type_lang_item}; use clippy_utils::ty::{implements_trait, is_type_lang_item};
@ -20,7 +20,6 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_middle::ty::Ty; use rustc_middle::ty::Ty;
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::def_id::DefId;
use rustc_span::edition::Edition::Edition2021; use rustc_span::edition::Edition::Edition2021;
use rustc_span::{sym, Span, Symbol}; use rustc_span::{sym, Span, Symbol};
@ -189,32 +188,18 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
&& is_format_macro(cx, macro_call.def_id) && is_format_macro(cx, macro_call.def_id)
&& let Some(format_args) = find_format_args(cx, expr, macro_call.expn) && let Some(format_args) = find_format_args(cx, expr, macro_call.expn)
{ {
for piece in &format_args.template { let linter = FormatArgsExpr {
if let FormatArgsPiece::Placeholder(placeholder) = piece cx,
&& let Ok(index) = placeholder.argument.index expr,
&& let Some(arg) = format_args.arguments.all_args().get(index) macro_call: &macro_call,
{ format_args: &format_args,
let arg_expr = find_format_arg_expr(expr, arg); ignore_mixed: self.ignore_mixed,
};
check_unused_format_specifier(cx, placeholder, arg_expr); linter.check_templates();
if placeholder.format_trait != FormatTrait::Display
|| placeholder.format_options != FormatOptions::default()
|| is_aliased(&format_args, index)
{
continue;
}
if let Ok(arg_hir_expr) = arg_expr {
let name = cx.tcx.item_name(macro_call.def_id);
check_format_in_format_args(cx, macro_call.span, name, arg_hir_expr);
check_to_string_in_format_args(cx, name, arg_hir_expr);
}
}
}
if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) { if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
check_uninlined_args(cx, &format_args, macro_call.span, macro_call.def_id, self.ignore_mixed); linter.check_uninlined_args();
} }
} }
} }
@ -222,255 +207,279 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
extract_msrv_attr!(LateContext); extract_msrv_attr!(LateContext);
} }
fn check_unused_format_specifier( struct FormatArgsExpr<'a, 'tcx> {
cx: &LateContext<'_>, cx: &'a LateContext<'tcx>,
placeholder: &FormatPlaceholder, expr: &'tcx Expr<'tcx>,
arg_expr: Result<&Expr<'_>, &rustc_ast::Expr>, macro_call: &'a MacroCall,
) { format_args: &'a rustc_ast::FormatArgs,
let ty_or_ast_expr = arg_expr.map(|expr| cx.typeck_results().expr_ty(expr).peel_refs()); ignore_mixed: bool,
}
let is_format_args = match ty_or_ast_expr { impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
Ok(ty) => is_type_lang_item(cx, ty, LangItem::FormatArguments), fn check_templates(&self) {
Err(expr) => matches!(expr.peel_parens_and_refs().kind, rustc_ast::ExprKind::FormatArgs(_)), for piece in &self.format_args.template {
}; if let FormatArgsPiece::Placeholder(placeholder) = piece
&& let Ok(index) = placeholder.argument.index
&& let Some(arg) = self.format_args.arguments.all_args().get(index)
{
let arg_expr = find_format_arg_expr(self.expr, arg);
let options = &placeholder.format_options; self.check_unused_format_specifier(placeholder, arg_expr);
let arg_span = match arg_expr { if let Ok(arg_expr) = arg_expr
Ok(expr) => expr.span, && placeholder.format_trait == FormatTrait::Display
Err(expr) => expr.span, && placeholder.format_options == FormatOptions::default()
}; && !self.is_aliased(index)
{
let name = self.cx.tcx.item_name(self.macro_call.def_id);
self.check_format_in_format_args(name, arg_expr);
self.check_to_string_in_format_args(name, arg_expr);
}
}
}
}
if let Some(placeholder_span) = placeholder.span fn check_unused_format_specifier(
&& is_format_args &self,
&& *options != FormatOptions::default() placeholder: &FormatPlaceholder,
{ arg_expr: Result<&Expr<'_>, &rustc_ast::Expr>,
span_lint_and_then( ) {
cx, let ty_or_ast_expr = arg_expr.map(|expr| self.cx.typeck_results().expr_ty(expr).peel_refs());
UNUSED_FORMAT_SPECS,
placeholder_span,
"format specifiers have no effect on `format_args!()`",
|diag| {
let mut suggest_format = |spec| {
let message = format!("for the {spec} to apply consider using `format!()`");
if let Some(mac_call) = root_macro_call(arg_span) let is_format_args = match ty_or_ast_expr {
&& cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id) Ok(ty) => is_type_lang_item(self.cx, ty, LangItem::FormatArguments),
{ Err(expr) => matches!(expr.peel_parens_and_refs().kind, rustc_ast::ExprKind::FormatArgs(_)),
diag.span_suggestion( };
cx.sess().source_map().span_until_char(mac_call.span, '!'),
message, let options = &placeholder.format_options;
"format",
let arg_span = match arg_expr {
Ok(expr) => expr.span,
Err(expr) => expr.span,
};
if let Some(placeholder_span) = placeholder.span
&& is_format_args
&& *options != FormatOptions::default()
{
span_lint_and_then(
self.cx,
UNUSED_FORMAT_SPECS,
placeholder_span,
"format specifiers have no effect on `format_args!()`",
|diag| {
let mut suggest_format = |spec| {
let message = format!("for the {spec} to apply consider using `format!()`");
if let Some(mac_call) = root_macro_call(arg_span)
&& self.cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id)
{
diag.span_suggestion(
self.cx.sess().source_map().span_until_char(mac_call.span, '!'),
message,
"format",
Applicability::MaybeIncorrect,
);
} else {
diag.help(message);
}
};
if options.width.is_some() {
suggest_format("width");
}
if options.precision.is_some() {
suggest_format("precision");
}
if let Some(format_span) = format_placeholder_format_span(placeholder) {
diag.span_suggestion_verbose(
format_span,
"if the current behavior is intentional, remove the format specifiers",
"",
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} else {
diag.help(message);
} }
}; },
);
}
}
if options.width.is_some() { fn check_uninlined_args(&self) {
suggest_format("width"); if self.format_args.span.from_expansion() {
} return;
}
if self.macro_call.span.edition() < Edition2021
&& (is_panic(self.cx, self.macro_call.def_id) || is_assert_macro(self.cx, self.macro_call.def_id))
{
// panic!, assert!, and debug_assert! before 2021 edition considers a single string argument as
// non-format
return;
}
if options.precision.is_some() { let mut fixes = Vec::new();
suggest_format("precision"); // If any of the arguments are referenced by an index number,
} // and that argument is not a simple variable and cannot be inlined,
// we cannot remove any other arguments in the format string,
// because the index numbers might be wrong after inlining.
// Example of an un-inlinable format: print!("{}{1}", foo, 2)
for (pos, usage) in self.format_arg_positions() {
if !self.check_one_arg(pos, usage, &mut fixes) {
return;
}
}
if let Some(format_span) = format_placeholder_format_span(placeholder) { if fixes.is_empty() {
diag.span_suggestion_verbose( return;
format_span, }
"if the current behavior is intentional, remove the format specifiers",
"", // multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
Applicability::MaybeIncorrect, // in those cases, make the code suggestion hidden
); let multiline_fix = fixes
} .iter()
.any(|(span, _)| self.cx.sess().source_map().is_multiline(*span));
// Suggest removing each argument only once, for example in `format!("{0} {0}", arg)`.
fixes.sort_unstable_by_key(|(span, _)| *span);
fixes.dedup_by_key(|(span, _)| *span);
span_lint_and_then(
self.cx,
UNINLINED_FORMAT_ARGS,
self.macro_call.span,
"variables can be used directly in the `format!` string",
|diag| {
diag.multipart_suggestion_with_style(
"change this to",
fixes,
Applicability::MachineApplicable,
if multiline_fix { CompletelyHidden } else { ShowCode },
);
}, },
); );
} }
}
fn check_uninlined_args( fn check_one_arg(&self, pos: &FormatArgPosition, usage: FormatParamUsage, fixes: &mut Vec<(Span, String)>) -> bool {
cx: &LateContext<'_>, let index = pos.index.unwrap();
args: &rustc_ast::FormatArgs, let arg = &self.format_args.arguments.all_args()[index];
call_site: Span,
def_id: DefId, if !matches!(arg.kind, FormatArgumentKind::Captured(_))
ignore_mixed: bool, && let rustc_ast::ExprKind::Path(None, path) = &arg.expr.kind
) { && let [segment] = path.segments.as_slice()
if args.span.from_expansion() { && segment.args.is_none()
return; && let Some(arg_span) = format_arg_removal_span(self.format_args, index)
} && let Some(pos_span) = pos.span
if call_site.edition() < Edition2021 && (is_panic(cx, def_id) || is_assert_macro(cx, def_id)) { {
// panic!, assert!, and debug_assert! before 2021 edition considers a single string argument as let replacement = match usage {
// non-format FormatParamUsage::Argument => segment.ident.name.to_string(),
return; FormatParamUsage::Width => format!("{}$", segment.ident.name),
FormatParamUsage::Precision => format!(".{}$", segment.ident.name),
};
fixes.push((pos_span, replacement));
fixes.push((arg_span, String::new()));
true // successful inlining, continue checking
} else {
// Do not continue inlining (return false) in case
// * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
// * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already
pos.kind != FormatArgPositionKind::Number
&& (!self.ignore_mixed || matches!(arg.kind, FormatArgumentKind::Captured(_)))
}
} }
let mut fixes = Vec::new(); fn check_format_in_format_args(&self, name: Symbol, arg: &Expr<'_>) {
// If any of the arguments are referenced by an index number, let expn_data = arg.span.ctxt().outer_expn_data();
// and that argument is not a simple variable and cannot be inlined, if expn_data.call_site.from_expansion() {
// we cannot remove any other arguments in the format string,
// because the index numbers might be wrong after inlining.
// Example of an un-inlinable format: print!("{}{1}", foo, 2)
for (pos, usage) in format_arg_positions(args) {
if !check_one_arg(args, pos, usage, &mut fixes, ignore_mixed) {
return; return;
} }
let Some(mac_id) = expn_data.macro_def_id else { return };
if !self.cx.tcx.is_diagnostic_item(sym::format_macro, mac_id) {
return;
}
span_lint_and_then(
self.cx,
FORMAT_IN_FORMAT_ARGS,
self.macro_call.span,
&format!("`format!` in `{name}!` args"),
|diag| {
diag.help(format!(
"combine the `format!(..)` arguments with the outer `{name}!(..)` call"
));
diag.help("or consider changing `format!` to `format_args!`");
},
);
} }
if fixes.is_empty() { fn check_to_string_in_format_args(&self, name: Symbol, value: &Expr<'_>) {
return; let cx = self.cx;
} if !value.span.from_expansion()
&& let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind
// multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308 && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id)
// in those cases, make the code suggestion hidden && is_diag_trait_item(cx, method_def_id, sym::ToString)
let multiline_fix = fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)); && let receiver_ty = cx.typeck_results().expr_ty(receiver)
&& let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display)
// Suggest removing each argument only once, for example in `format!("{0} {0}", arg)`. && let (n_needed_derefs, target) =
fixes.sort_unstable_by_key(|(span, _)| *span); count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter())
fixes.dedup_by_key(|(span, _)| *span); && implements_trait(cx, target, display_trait_id, &[])
&& let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait()
span_lint_and_then( && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
cx, {
UNINLINED_FORMAT_ARGS, let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
call_site, if n_needed_derefs == 0 && !needs_ref {
"variables can be used directly in the `format!` string", span_lint_and_sugg(
|diag| { cx,
diag.multipart_suggestion_with_style( TO_STRING_IN_FORMAT_ARGS,
"change this to", to_string_span.with_lo(receiver.span.hi()),
fixes, &format!("`to_string` applied to a type that implements `Display` in `{name}!` args"),
Applicability::MachineApplicable, "remove this",
if multiline_fix { CompletelyHidden } else { ShowCode }, String::new(),
); Applicability::MachineApplicable,
}, );
); } else {
} span_lint_and_sugg(
cx,
fn check_one_arg( TO_STRING_IN_FORMAT_ARGS,
args: &rustc_ast::FormatArgs, value.span,
pos: &FormatArgPosition, &format!("`to_string` applied to a type that implements `Display` in `{name}!` args"),
usage: FormatParamUsage, "use this",
fixes: &mut Vec<(Span, String)>, format!(
ignore_mixed: bool, "{}{:*>n_needed_derefs$}{receiver_snippet}",
) -> bool { if needs_ref { "&" } else { "" },
let index = pos.index.unwrap(); ""
let arg = &args.arguments.all_args()[index]; ),
Applicability::MachineApplicable,
if !matches!(arg.kind, FormatArgumentKind::Captured(_)) );
&& let rustc_ast::ExprKind::Path(None, path) = &arg.expr.kind }
&& let [segment] = path.segments.as_slice()
&& segment.args.is_none()
&& let Some(arg_span) = format_arg_removal_span(args, index)
&& let Some(pos_span) = pos.span
{
let replacement = match usage {
FormatParamUsage::Argument => segment.ident.name.to_string(),
FormatParamUsage::Width => format!("{}$", segment.ident.name),
FormatParamUsage::Precision => format!(".{}$", segment.ident.name),
};
fixes.push((pos_span, replacement));
fixes.push((arg_span, String::new()));
true // successful inlining, continue checking
} else {
// Do not continue inlining (return false) in case
// * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
// * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already
pos.kind != FormatArgPositionKind::Number
&& (!ignore_mixed || matches!(arg.kind, FormatArgumentKind::Captured(_)))
}
}
fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
let expn_data = arg.span.ctxt().outer_expn_data();
if expn_data.call_site.from_expansion() {
return;
}
let Some(mac_id) = expn_data.macro_def_id else { return };
if !cx.tcx.is_diagnostic_item(sym::format_macro, mac_id) {
return;
}
span_lint_and_then(
cx,
FORMAT_IN_FORMAT_ARGS,
call_site,
&format!("`format!` in `{name}!` args"),
|diag| {
diag.help(format!(
"combine the `format!(..)` arguments with the outer `{name}!(..)` call"
));
diag.help("or consider changing `format!` to `format_args!`");
},
);
}
fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) {
if !value.span.from_expansion()
&& let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id)
&& is_diag_trait_item(cx, method_def_id, sym::ToString)
&& let receiver_ty = cx.typeck_results().expr_ty(receiver)
&& let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display)
&& let (n_needed_derefs, target) =
count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter())
&& implements_trait(cx, target, display_trait_id, &[])
&& let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait()
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
{
let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
if n_needed_derefs == 0 && !needs_ref {
span_lint_and_sugg(
cx,
TO_STRING_IN_FORMAT_ARGS,
to_string_span.with_lo(receiver.span.hi()),
&format!("`to_string` applied to a type that implements `Display` in `{name}!` args"),
"remove this",
String::new(),
Applicability::MachineApplicable,
);
} else {
span_lint_and_sugg(
cx,
TO_STRING_IN_FORMAT_ARGS,
value.span,
&format!("`to_string` applied to a type that implements `Display` in `{name}!` args"),
"use this",
format!(
"{}{:*>n_needed_derefs$}{receiver_snippet}",
if needs_ref { "&" } else { "" },
""
),
Applicability::MachineApplicable,
);
} }
} }
}
fn format_arg_positions( fn format_arg_positions(&self) -> impl Iterator<Item = (&FormatArgPosition, FormatParamUsage)> {
format_args: &rustc_ast::FormatArgs, self.format_args.template.iter().flat_map(|piece| match piece {
) -> impl Iterator<Item = (&FormatArgPosition, FormatParamUsage)> { FormatArgsPiece::Placeholder(placeholder) => {
format_args.template.iter().flat_map(|piece| match piece { let mut positions = ArrayVec::<_, 3>::new();
FormatArgsPiece::Placeholder(placeholder) => {
let mut positions = ArrayVec::<_, 3>::new();
positions.push((&placeholder.argument, FormatParamUsage::Argument)); positions.push((&placeholder.argument, FormatParamUsage::Argument));
if let Some(FormatCount::Argument(position)) = &placeholder.format_options.width { if let Some(FormatCount::Argument(position)) = &placeholder.format_options.width {
positions.push((position, FormatParamUsage::Width)); positions.push((position, FormatParamUsage::Width));
} }
if let Some(FormatCount::Argument(position)) = &placeholder.format_options.precision { if let Some(FormatCount::Argument(position)) = &placeholder.format_options.precision {
positions.push((position, FormatParamUsage::Precision)); positions.push((position, FormatParamUsage::Precision));
} }
positions positions
}, },
FormatArgsPiece::Literal(_) => ArrayVec::new(), FormatArgsPiece::Literal(_) => ArrayVec::new(),
}) })
} }
/// Returns true if the format argument at `index` is referred to by multiple format params /// Returns true if the format argument at `index` is referred to by multiple format params
fn is_aliased(format_args: &rustc_ast::FormatArgs, index: usize) -> bool { fn is_aliased(&self, index: usize) -> bool {
format_arg_positions(format_args) self.format_arg_positions()
.filter(|(position, _)| position.index == Ok(index)) .filter(|(position, _)| position.index == Ok(index))
.at_most_one() .at_most_one()
.is_err() .is_err()
}
} }
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>) fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)

View file

@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::symbol::kw; use rustc_span::symbol::kw;
use rustc_span::{sym, Span, Symbol}; use rustc_span::{sym, Symbol};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -119,123 +119,132 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl {
} }
fn check_impl_item_post(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { fn check_impl_item_post(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
// Assume no nested Impl of Debug and Display within eachother // Assume no nested Impl of Debug and Display within each other
if is_format_trait_impl(cx, impl_item).is_some() { if is_format_trait_impl(cx, impl_item).is_some() {
self.format_trait_impl = None; self.format_trait_impl = None;
} }
} }
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let Some(format_trait_impl) = self.format_trait_impl else { if let Some(format_trait_impl) = self.format_trait_impl {
return; let linter = FormatImplExpr {
}; cx,
expr,
if format_trait_impl.name == sym::Display { format_trait_impl,
check_to_string_in_display(cx, expr); };
linter.check_to_string_in_display();
linter.check_self_in_format_args();
linter.check_print_in_format_impl();
} }
check_self_in_format_args(cx, expr, format_trait_impl);
check_print_in_format_impl(cx, expr, format_trait_impl);
} }
} }
fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) { struct FormatImplExpr<'a, 'tcx> {
if let ExprKind::MethodCall(path, self_arg, ..) = expr.kind cx: &'a LateContext<'tcx>,
// Get the hir_id of the object we are calling the method on expr: &'tcx Expr<'tcx>,
// Is the method to_string() ? format_trait_impl: FormatTraitNames,
&& path.ident.name == sym::to_string
// Is the method a part of the ToString trait? (i.e. not to_string() implemented
// separately)
&& let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
&& is_diag_trait_item(cx, expr_def_id, sym::ToString)
// Is the method is called on self
&& let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind
&& let [segment] = path.segments
&& segment.ident.name == kw::SelfLower
{
span_lint(
cx,
RECURSIVE_FORMAT_IMPL,
expr.span,
"using `self.to_string` in `fmt::Display` implementation will cause infinite recursion",
);
}
} }
fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTraitNames) { impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> {
// Check each arg in format calls - do we ever use Display on self (directly or via deref)? fn check_to_string_in_display(&self) {
if let Some(outer_macro) = root_macro_call_first_node(cx, expr) if self.format_trait_impl.name == sym::Display
&& let macro_def_id = outer_macro.def_id && let ExprKind::MethodCall(path, self_arg, ..) = self.expr.kind
&& is_format_macro(cx, macro_def_id) // Get the hir_id of the object we are calling the method on
&& let Some(format_args) = find_format_args(cx, expr, outer_macro.expn) // Is the method to_string() ?
{ && path.ident.name == sym::to_string
for piece in &format_args.template { // Is the method a part of the ToString trait? (i.e. not to_string() implemented
if let FormatArgsPiece::Placeholder(placeholder) = piece // separately)
&& let trait_name = match placeholder.format_trait { && let Some(expr_def_id) = self.cx.typeck_results().type_dependent_def_id(self.expr.hir_id)
FormatTrait::Display => sym::Display, && is_diag_trait_item(self.cx, expr_def_id, sym::ToString)
FormatTrait::Debug => sym::Debug, // Is the method is called on self
FormatTrait::LowerExp => sym!(LowerExp), && let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind
FormatTrait::UpperExp => sym!(UpperExp), && let [segment] = path.segments
FormatTrait::Octal => sym!(Octal), && segment.ident.name == kw::SelfLower
FormatTrait::Pointer => sym::Pointer, {
FormatTrait::Binary => sym!(Binary), span_lint(
FormatTrait::LowerHex => sym!(LowerHex), self.cx,
FormatTrait::UpperHex => sym!(UpperHex), RECURSIVE_FORMAT_IMPL,
self.expr.span,
"using `self.to_string` in `fmt::Display` implementation will cause infinite recursion",
);
}
}
fn check_self_in_format_args(&self) {
// Check each arg in format calls - do we ever use Display on self (directly or via deref)?
if let Some(outer_macro) = root_macro_call_first_node(self.cx, self.expr)
&& let macro_def_id = outer_macro.def_id
&& is_format_macro(self.cx, macro_def_id)
&& let Some(format_args) = find_format_args(self.cx, self.expr, outer_macro.expn)
{
for piece in &format_args.template {
if let FormatArgsPiece::Placeholder(placeholder) = piece
&& let trait_name = match placeholder.format_trait {
FormatTrait::Display => sym::Display,
FormatTrait::Debug => sym::Debug,
FormatTrait::LowerExp => sym!(LowerExp),
FormatTrait::UpperExp => sym!(UpperExp),
FormatTrait::Octal => sym!(Octal),
FormatTrait::Pointer => sym::Pointer,
FormatTrait::Binary => sym!(Binary),
FormatTrait::LowerHex => sym!(LowerHex),
FormatTrait::UpperHex => sym!(UpperHex),
}
&& trait_name == self.format_trait_impl.name
&& let Ok(index) = placeholder.argument.index
&& let Some(arg) = format_args.arguments.all_args().get(index)
&& let Ok(arg_expr) = find_format_arg_expr(self.expr, arg)
{
self.check_format_arg_self(arg_expr);
} }
&& trait_name == impl_trait.name
&& let Ok(index) = placeholder.argument.index
&& let Some(arg) = format_args.arguments.all_args().get(index)
&& let Ok(arg_expr) = find_format_arg_expr(expr, arg)
{
check_format_arg_self(cx, expr.span, arg_expr, impl_trait);
} }
} }
} }
}
fn check_format_arg_self(cx: &LateContext<'_>, span: Span, arg: &Expr<'_>, impl_trait: FormatTraitNames) { fn check_format_arg_self(&self, arg: &Expr<'_>) {
// Handle multiple dereferencing of references e.g. &&self // Handle multiple dereferencing of references e.g. &&self
// Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl) // Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
// Since the argument to fmt is itself a reference: &self // Since the argument to fmt is itself a reference: &self
let reference = peel_ref_operators(cx, arg); let reference = peel_ref_operators(self.cx, arg);
let map = cx.tcx.hir(); let map = self.cx.tcx.hir();
// Is the reference self? // Is the reference self?
if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) { if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
let FormatTraitNames { name, .. } = impl_trait; let FormatTraitNames { name, .. } = self.format_trait_impl;
span_lint( span_lint(
cx, self.cx,
RECURSIVE_FORMAT_IMPL, RECURSIVE_FORMAT_IMPL,
span, self.expr.span,
&format!("using `self` as `{name}` in `impl {name}` will cause infinite recursion"), &format!("using `self` as `{name}` in `impl {name}` will cause infinite recursion"),
); );
}
} }
}
fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTraitNames) { fn check_print_in_format_impl(&self) {
if let Some(macro_call) = root_macro_call_first_node(cx, expr) if let Some(macro_call) = root_macro_call_first_node(self.cx, self.expr)
&& let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id) && let Some(name) = self.cx.tcx.get_diagnostic_name(macro_call.def_id)
{ {
let replacement = match name { let replacement = match name {
sym::print_macro | sym::eprint_macro => "write", sym::print_macro | sym::eprint_macro => "write",
sym::println_macro | sym::eprintln_macro => "writeln", sym::println_macro | sym::eprintln_macro => "writeln",
_ => return, _ => return,
}; };
let name = name.as_str().strip_suffix("_macro").unwrap(); let name = name.as_str().strip_suffix("_macro").unwrap();
span_lint_and_sugg( span_lint_and_sugg(
cx, self.cx,
PRINT_IN_FORMAT_IMPL, PRINT_IN_FORMAT_IMPL,
macro_call.span, macro_call.span,
&format!("use of `{name}!` in `{}` impl", impl_trait.name), &format!("use of `{name}!` in `{}` impl", self.format_trait_impl.name),
"replace with", "replace with",
if let Some(formatter_name) = impl_trait.formatter_name { if let Some(formatter_name) = self.format_trait_impl.formatter_name {
format!("{replacement}!({formatter_name}, ..)") format!("{replacement}!({formatter_name}, ..)")
} else { } else {
format!("{replacement}!(..)") format!("{replacement}!(..)")
}, },
Applicability::HasPlaceholders, Applicability::HasPlaceholders,
); );
}
} }
} }

View file

@ -181,6 +181,9 @@ fn convert_to_from(
let from = snippet_opt(cx, self_ty.span)?; let from = snippet_opt(cx, self_ty.span)?;
let into = snippet_opt(cx, target_ty.span)?; let into = snippet_opt(cx, target_ty.span)?;
let return_type = matches!(sig.decl.output, FnRetTy::Return(_))
.then_some(String::from("Self"))
.unwrap_or_default();
let mut suggestions = vec![ let mut suggestions = vec![
// impl Into<T> for U -> impl From<T> for U // 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 // fn into([mut] self) -> T -> fn into([mut] v: T) -> T
// ~~~~ ~~~~ // ~~~~ ~~~~
(self_ident.span, format!("val: {from}")), (self_ident.span, format!("val: {from}")),
];
if let FnRetTy::Return(_) = sig.decl.output {
// fn into(self) -> T -> fn into(self) -> Self // 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 { let mut finder = SelfFinder {
cx, cx,

View file

@ -1,16 +1,17 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use rustc_errors::{Applicability, SuggestionStyle}; use rustc_errors::{Applicability, SuggestionStyle};
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{ use rustc_hir::{
Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, TraitBoundModifier, TraitItem, Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, OpaqueTy, TraitBoundModifier,
TraitItemKind, TyKind, TraitItem, TraitItemKind, TyKind, TypeBinding,
}; };
use rustc_hir_analysis::hir_ty_to_ty; use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt}; use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::def_id::LocalDefId;
use rustc_span::Span; use rustc_span::Span;
declare_clippy_lint! { declare_clippy_lint! {
@ -50,20 +51,17 @@ declare_clippy_lint! {
} }
declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]); declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]);
#[allow(clippy::too_many_arguments)]
fn emit_lint( fn emit_lint(
cx: &LateContext<'_>, cx: &LateContext<'_>,
poly_trait: &rustc_hir::PolyTraitRef<'_>, poly_trait: &rustc_hir::PolyTraitRef<'_>,
opaque_ty: &rustc_hir::OpaqueTy<'_>, opaque_ty: &rustc_hir::OpaqueTy<'_>,
index: usize, index: usize,
// The bindings that were implied // The bindings that were implied, used for suggestion purposes since removing a bound with associated types
// means we might need to then move it to a different bound
implied_bindings: &[rustc_hir::TypeBinding<'_>], implied_bindings: &[rustc_hir::TypeBinding<'_>],
// The original bindings that `implied_bindings` are implied from bound: &ImplTraitBound<'_>,
implied_by_bindings: &[rustc_hir::TypeBinding<'_>],
implied_by_args: &[GenericArg<'_>],
implied_by_span: Span,
) { ) {
let implied_by = snippet(cx, implied_by_span, ".."); let implied_by = snippet(cx, bound.span, "..");
span_lint_and_then( span_lint_and_then(
cx, cx,
@ -93,17 +91,17 @@ fn emit_lint(
// If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut` // If we're going to suggest removing `Deref<..>`, we'll need to put `<Target = u8>` on `DerefMut`
let omitted_assoc_tys: Vec<_> = implied_bindings let omitted_assoc_tys: Vec<_> = implied_bindings
.iter() .iter()
.filter(|binding| !implied_by_bindings.iter().any(|b| b.ident == binding.ident)) .filter(|binding| !bound.bindings.iter().any(|b| b.ident == binding.ident))
.collect(); .collect();
if !omitted_assoc_tys.is_empty() { if !omitted_assoc_tys.is_empty() {
// `<>` needs to be added if there aren't yet any generic arguments or bindings // `<>` needs to be added if there aren't yet any generic arguments or bindings
let needs_angle_brackets = implied_by_args.is_empty() && implied_by_bindings.is_empty(); let needs_angle_brackets = bound.args.is_empty() && bound.bindings.is_empty();
let insert_span = match (implied_by_args, implied_by_bindings) { let insert_span = match (bound.args, bound.bindings) {
([.., arg], [.., binding]) => arg.span().max(binding.span).shrink_to_hi(), ([.., arg], [.., binding]) => arg.span().max(binding.span).shrink_to_hi(),
([.., arg], []) => arg.span().shrink_to_hi(), ([.., arg], []) => arg.span().shrink_to_hi(),
([], [.., binding]) => binding.span.shrink_to_hi(), ([], [.., binding]) => binding.span.shrink_to_hi(),
([], []) => implied_by_span.shrink_to_hi(), ([], []) => bound.span.shrink_to_hi(),
}; };
let mut associated_tys_sugg = if needs_angle_brackets { let mut associated_tys_sugg = if needs_angle_brackets {
@ -223,42 +221,93 @@ fn is_same_generics<'tcx>(
}) })
} }
struct ImplTraitBound<'tcx> {
/// The span of the bound in the `impl Trait` type
span: Span,
/// The predicates defined in the trait referenced by this bound. This also contains the actual
/// supertrait bounds
predicates: &'tcx [(ty::Clause<'tcx>, Span)],
/// The `DefId` of the trait being referenced by this bound
trait_def_id: DefId,
/// The generic arguments on the `impl Trait` bound
args: &'tcx [GenericArg<'tcx>],
/// The associated types on this bound
bindings: &'tcx [TypeBinding<'tcx>],
}
/// Given an `impl Trait` type, gets all the supertraits from each bound ("implied bounds").
///
/// For `impl Deref + DerefMut + Eq` this returns `[Deref, PartialEq]`.
/// The `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`, and `PartialEq` comes from
/// `Eq`.
fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, opaque_ty: &OpaqueTy<'tcx>) -> Vec<ImplTraitBound<'tcx>> {
opaque_ty
.bounds
.iter()
.filter_map(|bound| {
if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
&& let [.., path] = poly_trait.trait_ref.path.segments
&& poly_trait.bound_generic_params.is_empty()
&& let Some(trait_def_id) = path.res.opt_def_id()
&& let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
// If the trait has no supertrait, there is no need to collect anything from that bound
&& !predicates.is_empty()
{
Some(ImplTraitBound {
predicates,
args: path.args.map_or([].as_slice(), |p| p.args),
bindings: path.args.map_or([].as_slice(), |p| p.bindings),
trait_def_id,
span: bound.span(),
})
} else {
None
}
})
.collect()
}
/// Given a bound in an `impl Trait` type, looks for a trait in the set of supertraits (previously
/// collected in [`collect_supertrait_bounds`]) that matches (same trait and generic arguments).
fn find_bound_in_supertraits<'a, 'tcx>(
cx: &LateContext<'tcx>,
trait_def_id: DefId,
args: &'tcx [GenericArg<'tcx>],
bounds: &'a [ImplTraitBound<'tcx>],
) -> Option<&'a ImplTraitBound<'tcx>> {
bounds.iter().find(|bound| {
bound.predicates.iter().any(|(clause, _)| {
if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
&& tr.def_id() == trait_def_id
{
is_same_generics(
cx.tcx,
tr.trait_ref.args,
bound.args,
args,
bound.trait_def_id,
trait_def_id,
)
} else {
false
}
})
})
}
fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) { fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
if let FnRetTy::Return(ty) = decl.output if let FnRetTy::Return(ty) = decl.output
&&let TyKind::OpaqueDef(item_id, ..) = ty.kind && let TyKind::OpaqueDef(item_id, ..) = ty.kind
&& let item = cx.tcx.hir().item(item_id) && let item = cx.tcx.hir().item(item_id)
&& let ItemKind::OpaqueTy(opaque_ty) = item.kind && let ItemKind::OpaqueTy(opaque_ty) = item.kind
// Very often there is only a single bound, e.g. `impl Deref<..>`, in which case // Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
// we can avoid doing a bunch of stuff unnecessarily. // we can avoid doing a bunch of stuff unnecessarily.
&& opaque_ty.bounds.len() > 1 && opaque_ty.bounds.len() > 1
{ {
// Get all the (implied) trait predicates in the bounds. let supertraits = collect_supertrait_bounds(cx, opaque_ty);
// For `impl Deref + DerefMut` this will contain [`Deref`].
// The implied `Deref` comes from `DerefMut` because `trait DerefMut: Deref {}`.
// N.B. (G)ATs are fine to disregard, because they must be the same for all of its supertraits.
// Example:
// `impl Deref<Target = i32> + DerefMut<Target = u32>` is not allowed.
// `DerefMut::Target` needs to match `Deref::Target`.
let implied_bounds: Vec<_> = opaque_ty
.bounds
.iter()
.filter_map(|bound| {
if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
&& let [.., path] = poly_trait.trait_ref.path.segments
&& poly_trait.bound_generic_params.is_empty()
&& let Some(trait_def_id) = path.res.opt_def_id()
&& let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
&& !predicates.is_empty()
// If the trait has no supertrait, there is nothing to add.
{
Some((bound.span(), path, predicates, trait_def_id))
} else {
None
}
})
.collect();
// Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec. // Lint all bounds in the `impl Trait` type that we've previously also seen in the set of
// supertraits of each of the bounds.
// This involves some extra logic when generic arguments are present, since // This involves some extra logic when generic arguments are present, since
// simply comparing trait `DefId`s won't be enough. We also need to compare the generics. // simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
for (index, bound) in opaque_ty.bounds.iter().enumerate() { for (index, bound) in opaque_ty.bounds.iter().enumerate() {
@ -267,42 +316,18 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
&& let implied_args = path.args.map_or([].as_slice(), |a| a.args) && let implied_args = path.args.map_or([].as_slice(), |a| a.args)
&& let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings) && let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings)
&& let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id() && let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
&& let Some((implied_by_span, implied_by_args, implied_by_bindings)) = && let Some(bound) = find_bound_in_supertraits(cx, def_id, implied_args, &supertraits)
implied_bounds // If the implied bound has a type binding that also exists in the implied-by trait,
.iter() // then we shouldn't lint. See #11880 for an example.
.find_map(|&(span, implied_by_path, preds, implied_by_def_id)| { && let assocs = cx.tcx.associated_items(bound.trait_def_id)
let implied_by_args = implied_by_path.args.map_or([].as_slice(), |a| a.args); && !implied_bindings.iter().any(|binding| {
let implied_by_bindings = implied_by_path.args.map_or([].as_slice(), |a| a.bindings); assocs
.filter_by_name_unhygienic(binding.ident.name)
preds.iter().find_map(|(clause, _)| { .next()
if let ClauseKind::Trait(tr) = clause.kind().skip_binder() .is_some_and(|assoc| assoc.kind == ty::AssocKind::Type)
&& tr.def_id() == def_id })
&& is_same_generics(
cx.tcx,
tr.trait_ref.args,
implied_by_args,
implied_args,
implied_by_def_id,
def_id,
)
{
Some((span, implied_by_args, implied_by_bindings))
} else {
None
}
})
})
{ {
emit_lint( emit_lint(cx, poly_trait, opaque_ty, index, implied_bindings, bound);
cx,
poly_trait,
opaque_ty,
index,
implied_bindings,
implied_by_bindings,
implied_by_args,
implied_by_span,
);
} }
} }
} }

View file

@ -1,14 +1,15 @@
use clippy_config::msrvs::Msrv; use clippy_config::msrvs::Msrv;
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_in_test_function;
use rustc_attr::{StabilityLevel, StableSince}; use rustc_attr::{StabilityLevel, StableSince};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind, HirId};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_span::Span; use rustc_span::{ExpnKind, Span};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -81,13 +82,18 @@ impl IncompatibleMsrv {
version version
} }
fn emit_lint_if_under_msrv(&mut self, cx: &LateContext<'_>, def_id: DefId, span: Span) { fn emit_lint_if_under_msrv(&mut self, cx: &LateContext<'_>, def_id: DefId, node: HirId, span: Span) {
if def_id.is_local() { if def_id.is_local() {
// We don't check local items since their MSRV is supposed to always be valid. // We don't check local items since their MSRV is supposed to always be valid.
return; return;
} }
let version = self.get_def_id_version(cx.tcx, def_id); let version = self.get_def_id_version(cx.tcx, def_id);
if self.msrv.meets(version) { if self.msrv.meets(version) || is_in_test_function(cx.tcx, node) {
return;
}
if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = span.ctxt().outer_expn_data().kind {
// Desugared expressions get to cheat and stability is ignored.
// Intentionally not using `.from_expansion()`, since we do still care about macro expansions
return; return;
} }
self.emit_lint_for(cx, span, version); self.emit_lint_for(cx, span, version);
@ -117,14 +123,14 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv {
match expr.kind { match expr.kind {
ExprKind::MethodCall(_, _, _, span) => { ExprKind::MethodCall(_, _, _, span) => {
if let Some(method_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { if let Some(method_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
self.emit_lint_if_under_msrv(cx, method_did, span); self.emit_lint_if_under_msrv(cx, method_did, expr.hir_id, span);
} }
}, },
ExprKind::Call(call, [_]) => { ExprKind::Call(call, [_]) => {
if let ExprKind::Path(qpath) = call.kind if let ExprKind::Path(qpath) = call.kind
&& let Some(path_def_id) = cx.qpath_res(&qpath, call.hir_id).opt_def_id() && let Some(path_def_id) = cx.qpath_res(&qpath, call.hir_id).opt_def_id()
{ {
self.emit_lint_if_under_msrv(cx, path_def_id, call.span); self.emit_lint_if_under_msrv(cx, path_def_id, expr.hir_id, call.span);
} }
}, },
_ => {}, _ => {},

View file

@ -255,7 +255,9 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> {
&& let hir::Node::Expr(maybe_addrof_expr) = cx.tcx.parent_hir_node(parent_id) && let hir::Node::Expr(maybe_addrof_expr) = cx.tcx.parent_hir_node(parent_id)
&& let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind && let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind
{ {
use_info.index_use.push((index_value, cx.tcx.hir().span(parent_expr.hir_id))); use_info
.index_use
.push((index_value, cx.tcx.hir().span(parent_expr.hir_id)));
return; return;
} }

View file

@ -174,6 +174,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
// only `usize` index is legal in rust array index // only `usize` index is legal in rust array index
// leave other type to rustc // leave other type to rustc
if let Constant::Int(off) = constant if let Constant::Int(off) = constant
&& off <= usize::MAX as u128
&& let ty::Uint(utype) = cx.typeck_results().expr_ty(index).kind() && let ty::Uint(utype) = cx.typeck_results().expr_ty(index).kind()
&& *utype == ty::UintTy::Usize && *utype == ty::UintTy::Usize
&& let ty::Array(_, s) = ty.kind() && let ty::Array(_, s) = ty.kind()

View file

@ -385,7 +385,6 @@ impl LateLintPass<'_> for ItemNameRepetitions {
assert!(last.is_some()); assert!(last.is_some());
} }
#[expect(clippy::similar_names)]
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
let item_name = item.ident.name.as_str(); let item_name = item.ident.name.as_str();
let item_camel = to_camel_case(item_name); let item_camel = to_camel_case(item_name);

View file

@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator {
if matches!(name, "iter" | "iter_mut") if matches!(name, "iter" | "iter_mut")
&& !matches!( && !matches!(
get_parent_node(cx.tcx, item.hir_id()), get_parent_node(cx.tcx, item.hir_id()),
Some(Node::Item(Item { kind: ItemKind::Impl(i), .. })) if i.of_trait.is_some() Node::Item(Item { kind: ItemKind::Impl(i), .. }) if i.of_trait.is_some()
) )
{ {
if let ImplItemKind::Fn(fn_sig, _) = &item.kind { if let ImplItemKind::Fn(fn_sig, _) = &item.kind {

View file

@ -14,7 +14,7 @@
clippy::missing_docs_in_private_items, clippy::missing_docs_in_private_items,
clippy::must_use_candidate, clippy::must_use_candidate,
rustc::diagnostic_outside_of_impl, rustc::diagnostic_outside_of_impl,
rustc::untranslatable_diagnostic, rustc::untranslatable_diagnostic
)] )]
#![warn(trivial_casts, trivial_numeric_casts)] #![warn(trivial_casts, trivial_numeric_casts)]
// warn on lints, that are included in `rust-lang/rust`s bootstrap // warn on lints, that are included in `rust-lang/rust`s bootstrap

View file

@ -1,17 +1,18 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{fn_def_id, is_lint_allowed}; use clippy_utils::{fn_def_id, is_from_proc_macro, is_lint_allowed};
use hir::intravisit::{walk_expr, Visitor}; use hir::intravisit::{walk_expr, Visitor};
use hir::{Expr, ExprKind, FnRetTy, FnSig, Node}; use hir::{Expr, ExprKind, FnRetTy, FnSig, Node};
use rustc_ast::Label; use rustc_ast::Label;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use super::INFINITE_LOOP; use super::INFINITE_LOOP;
pub(super) fn check<'tcx>( pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
expr: &Expr<'_>, expr: &Expr<'tcx>,
loop_block: &'tcx hir::Block<'_>, loop_block: &'tcx hir::Block<'_>,
label: Option<Label>, label: Option<Label>,
) { ) {
@ -34,6 +35,10 @@ pub(super) fn check<'tcx>(
return; return;
} }
if in_external_macro(cx.sess(), expr.span) || is_from_proc_macro(cx, expr) {
return;
}
let mut loop_visitor = LoopVisitor { let mut loop_visitor = LoopVisitor {
cx, cx,
label, label,

View file

@ -36,7 +36,8 @@ struct PathAndSpan {
span: Span, span: Span,
} }
/// `MacroRefData` includes the name of the macro. /// `MacroRefData` includes the name of the macro
/// and the path from `SourceMap::span_to_filename`.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MacroRefData { pub struct MacroRefData {
name: String, name: String,

View file

@ -64,38 +64,50 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
let min_index = usize::min(lindex, rindex); let min_index = usize::min(lindex, rindex);
let max_index = usize::max(lindex, rindex); let max_index = usize::max(lindex, rindex);
let mut local_map: HirIdMap<HirId> = HirIdMap::default(); let check_eq_with_pat = |expr_a: &Expr<'_>, expr_b: &Expr<'_>| {
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { let mut local_map: HirIdMap<HirId> = HirIdMap::default();
if let Some(a_id) = path_to_local(a) let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
&& let Some(b_id) = path_to_local(b) if let Some(a_id) = path_to_local(a)
&& let entry = match local_map.entry(a_id) { && let Some(b_id) = path_to_local(b)
HirIdMapEntry::Vacant(entry) => entry, && let entry = match local_map.entry(a_id) {
// check if using the same bindings as before HirIdMapEntry::Vacant(entry) => entry,
HirIdMapEntry::Occupied(entry) => return *entry.get() == b_id, // check if using the same bindings as before
} HirIdMapEntry::Occupied(entry) => return *entry.get() == b_id,
}
// the names technically don't have to match; this makes the lint more conservative // the names technically don't have to match; this makes the lint more conservative
&& cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id) && cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id)
&& cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b) && cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b)
&& pat_contains_local(lhs.pat, a_id) && pat_contains_local(lhs.pat, a_id)
&& pat_contains_local(rhs.pat, b_id) && pat_contains_local(rhs.pat, b_id)
{ {
entry.insert(b_id); entry.insert(b_id);
true true
} else { } else {
false false
} }
}; };
// Arms with a guard are ignored, those cant always be merged together
// If both arms overlap with an arm in between then these can't be merged either. SpanlessEq::new(cx)
!(backwards_blocking_idxs[max_index] > min_index && forwards_blocking_idxs[min_index] < max_index) .expr_fallback(eq_fallback)
&& lhs.guard.is_none() .eq_expr(expr_a, expr_b)
&& rhs.guard.is_none()
&& SpanlessEq::new(cx)
.expr_fallback(eq_fallback)
.eq_expr(lhs.body, rhs.body)
// these checks could be removed to allow unused bindings // these checks could be removed to allow unused bindings
&& bindings_eq(lhs.pat, local_map.keys().copied().collect()) && bindings_eq(lhs.pat, local_map.keys().copied().collect())
&& bindings_eq(rhs.pat, local_map.values().copied().collect()) && bindings_eq(rhs.pat, local_map.values().copied().collect())
};
let check_same_guard = || match (&lhs.guard, &rhs.guard) {
(None, None) => true,
(Some(lhs_guard), Some(rhs_guard)) => check_eq_with_pat(lhs_guard, rhs_guard),
_ => false,
};
let check_same_body = || check_eq_with_pat(lhs.body, rhs.body);
// Arms with different guard are ignored, those cant always be merged together
// If both arms overlap with an arm in between then these can't be merged either.
!(backwards_blocking_idxs[max_index] > min_index && forwards_blocking_idxs[min_index] < max_index)
&& check_same_guard()
&& check_same_body()
}; };
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();

View file

@ -123,37 +123,35 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
/// Manually check for coercion casting by checking if the type of the match operand or let expr /// Manually check for coercion casting by checking if the type of the match operand or let expr
/// differs with the assigned local variable or the function return type. /// differs with the assigned local variable or the function return type.
fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool { fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool {
if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) { match get_parent_node(cx.tcx, p_expr.hir_id) {
match p_node { // Compare match_expr ty with local in `let local = match match_expr {..}`
// Compare match_expr ty with local in `let local = match match_expr {..}` Node::Local(local) => {
Node::Local(local) => { let results = cx.typeck_results();
let results = cx.typeck_results(); return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr));
return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr)); },
}, // compare match_expr ty with RetTy in `fn foo() -> RetTy`
// compare match_expr ty with RetTy in `fn foo() -> RetTy` Node::Item(item) => {
Node::Item(item) => { if let ItemKind::Fn(..) = item.kind {
if let ItemKind::Fn(..) = item.kind { let output = cx
let output = cx .tcx
.tcx .fn_sig(item.owner_id)
.fn_sig(item.owner_id) .instantiate_identity()
.instantiate_identity() .output()
.output() .skip_binder();
.skip_binder(); return same_type_and_consts(output, cx.typeck_results().expr_ty(expr));
return same_type_and_consts(output, cx.typeck_results().expr_ty(expr)); }
} },
}, // check the parent expr for this whole block `{ match match_expr {..} }`
// check the parent expr for this whole block `{ match match_expr {..} }` Node::Block(block) => {
Node::Block(block) => { if let Some(block_parent_expr) = get_parent_expr_for_hir(cx, block.hir_id) {
if let Some(block_parent_expr) = get_parent_expr_for_hir(cx, block.hir_id) { return expr_ty_matches_p_ty(cx, expr, block_parent_expr);
return expr_ty_matches_p_ty(cx, expr, block_parent_expr); }
} },
}, // recursively call on `if xxx {..}` etc.
// recursively call on `if xxx {..}` etc. Node::Expr(p_expr) => {
Node::Expr(p_expr) => { return expr_ty_matches_p_ty(cx, expr, p_expr);
return expr_ty_matches_p_ty(cx, expr, p_expr); },
}, _ => {},
_ => {},
}
} }
false false
} }

View file

@ -5,7 +5,7 @@ use clippy_utils::visitors::{for_each_expr, is_local_used};
use rustc_ast::{BorrowKind, LitKind}; use rustc_ast::{BorrowKind, LitKind};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, Pat, PatKind}; use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, Pat, PatKind, UnOp};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
@ -269,7 +269,11 @@ fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Ctor(..), ..), Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Ctor(..), ..),
) )
}, },
ExprKind::AddrOf(..) | ExprKind::Array(..) | ExprKind::Tup(..) | ExprKind::Struct(..) => true, ExprKind::AddrOf(..)
| ExprKind::Array(..)
| ExprKind::Tup(..)
| ExprKind::Struct(..)
| ExprKind::Unary(UnOp::Neg, _) => true,
ExprKind::Lit(lit) if !matches!(lit.node, LitKind::Float(..)) => true, ExprKind::Lit(lit) if !matches!(lit.node, LitKind::Float(..)) => true,
_ => false, _ => false,
} { } {

View file

@ -37,7 +37,12 @@ pub(super) fn check<'tcx>(
} }
} }
fn set_diagnostic<'tcx>(diag: &mut DiagnosticBuilder<'_, ()>, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) { fn set_diagnostic<'tcx>(
diag: &mut DiagnosticBuilder<'_, ()>,
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
found: FoundSigDrop,
) {
if found.lint_suggestion == LintSuggestion::MoveAndClone { if found.lint_suggestion == LintSuggestion::MoveAndClone {
// If our suggestion is to move and clone, then we want to leave it to the user to // If our suggestion is to move and clone, then we want to leave it to the user to
// decide how to address this lint, since it may be that cloning is inappropriate. // decide how to address this lint, since it may be that cloning is inappropriate.

View file

@ -3,7 +3,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lin
use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_non_aggregate_primitive_type; use clippy_utils::ty::is_non_aggregate_primitive_type;
use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators, std_or_core}; use clippy_utils::{
is_default_equivalent, is_expr_used_or_unified, is_res_lang_ctor, path_res, peel_ref_operators, std_or_core,
};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionNone; use rustc_hir::LangItem::OptionNone;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
@ -232,7 +234,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
// Check that second argument is `Option::None` // Check that second argument is `Option::None`
if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
check_replace_option_with_none(cx, dest, expr.span); check_replace_option_with_none(cx, dest, expr.span);
} else if self.msrv.meets(msrvs::MEM_TAKE) { } else if self.msrv.meets(msrvs::MEM_TAKE) && is_expr_used_or_unified(cx.tcx, expr) {
check_replace_with_default(cx, src, dest, expr.span); check_replace_with_default(cx, src, dest, expr.span);
} }
check_replace_with_uninit(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span);

View file

@ -38,6 +38,7 @@ pub(super) fn check<'tcx>(
&& ext_str.starts_with('.') && ext_str.starts_with('.')
&& (ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) && (ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
|| ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit())) || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()))
&& !ext_str.chars().skip(1).all(|c| c.is_ascii_digit())
&& let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs() && let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs()
&& (recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String)) && (recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String))
{ {

View file

@ -49,7 +49,7 @@ pub(super) fn check(
if is_copy(cx, ty) { if is_copy(cx, ty) {
let parent_is_suffix_expr = match get_parent_node(cx.tcx, expr.hir_id) { let parent_is_suffix_expr = match get_parent_node(cx.tcx, expr.hir_id) {
Some(Node::Expr(parent)) => match parent.kind { Node::Expr(parent) => match parent.kind {
// &*x is a nop, &x.clone() is not // &*x is a nop, &x.clone() is not
ExprKind::AddrOf(..) => return, ExprKind::AddrOf(..) => return,
// (*x).func() is useless, x.clone().func() can work in case func borrows self // (*x).func() is useless, x.clone().func() can work in case func borrows self
@ -70,7 +70,7 @@ pub(super) fn check(
_ => false, _ => false,
}, },
// local binding capturing a reference // local binding capturing a reference
Some(Node::Local(l)) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..)) => { Node::Local(l) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..)) => {
return; return;
}, },
_ => false, _ => false,

View file

@ -28,104 +28,102 @@ pub(super) fn check<'tcx>(
iter_expr: &'tcx Expr<'tcx>, iter_expr: &'tcx Expr<'tcx>,
call_span: Span, call_span: Span,
) { ) {
if let Some(parent) = get_parent_node(cx.tcx, collect_expr.hir_id) { match get_parent_node(cx.tcx, collect_expr.hir_id) {
match parent { Node::Expr(parent) => {
Node::Expr(parent) => { check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr);
check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr);
if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind { if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let name = name.ident.as_str(); let name = name.ident.as_str();
let collect_ty = cx.typeck_results().expr_ty(collect_expr); let collect_ty = cx.typeck_results().expr_ty(collect_expr);
let sugg: String = match name { let sugg: String = match name {
"len" => { "len" => {
if let Some(adt) = collect_ty.ty_adt_def() if let Some(adt) = collect_ty.ty_adt_def()
&& matches!( && matches!(
cx.tcx.get_diagnostic_name(adt.did()), cx.tcx.get_diagnostic_name(adt.did()),
Some(sym::Vec | sym::VecDeque | sym::LinkedList | sym::BinaryHeap) Some(sym::Vec | sym::VecDeque | sym::LinkedList | sym::BinaryHeap)
) )
{
"count()".into()
} else {
return;
}
},
"is_empty"
if is_is_empty_sig(cx, parent.hir_id)
&& iterates_same_ty(cx, cx.typeck_results().expr_ty(iter_expr), collect_ty) =>
{ {
"next().is_none()".into() "count()".into()
}, } else {
"contains" => { return;
if is_contains_sig(cx, parent.hir_id, iter_expr) }
&& let Some(arg) = args.first() },
{ "is_empty"
let (span, prefix) = if let ExprKind::AddrOf(_, _, arg) = arg.kind { if is_is_empty_sig(cx, parent.hir_id)
(arg.span, "") && iterates_same_ty(cx, cx.typeck_results().expr_ty(iter_expr), collect_ty) =>
} else { {
(arg.span, "*") "next().is_none()".into()
}; },
let snip = snippet_with_applicability(cx, span, "??", &mut app); "contains" => {
format!("any(|x| x == {prefix}{snip})") if is_contains_sig(cx, parent.hir_id, iter_expr)
&& let Some(arg) = args.first()
{
let (span, prefix) = if let ExprKind::AddrOf(_, _, arg) = arg.kind {
(arg.span, "")
} else { } else {
return; (arg.span, "*")
} };
}, let snip = snippet_with_applicability(cx, span, "??", &mut app);
_ => return, format!("any(|x| x == {prefix}{snip})")
}; } else {
return;
}
},
_ => return,
};
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
NEEDLESS_COLLECT, NEEDLESS_COLLECT,
call_span.with_hi(parent.span.hi()), call_span.with_hi(parent.span.hi()),
NEEDLESS_COLLECT_MSG, NEEDLESS_COLLECT_MSG,
"replace with", "replace with",
sugg, sugg,
app, app,
); );
}
},
Node::Local(l) => {
if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) = l.pat.kind
&& let ty = cx.typeck_results().expr_ty(collect_expr)
&& [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList]
.into_iter()
.any(|item| is_type_diagnostic_item(cx, ty, item))
&& let iter_ty = cx.typeck_results().expr_ty(iter_expr)
&& let Some(block) = get_enclosing_block(cx, l.hir_id)
&& let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))
&& let [iter_call] = &*iter_calls
{
let mut used_count_visitor = UsedCountVisitor { cx, id, count: 0 };
walk_block(&mut used_count_visitor, block);
if used_count_visitor.count > 1 {
return;
} }
},
Node::Local(l) => {
if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) = l.pat.kind
&& let ty = cx.typeck_results().expr_ty(collect_expr)
&& [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList]
.into_iter()
.any(|item| is_type_diagnostic_item(cx, ty, item))
&& let iter_ty = cx.typeck_results().expr_ty(iter_expr)
&& let Some(block) = get_enclosing_block(cx, l.hir_id)
&& let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))
&& let [iter_call] = &*iter_calls
{
let mut used_count_visitor = UsedCountVisitor { cx, id, count: 0 };
walk_block(&mut used_count_visitor, block);
if used_count_visitor.count > 1 {
return;
}
// Suggest replacing iter_call with iter_replacement, and removing stmt // Suggest replacing iter_call with iter_replacement, and removing stmt
let mut span = MultiSpan::from_span(name_span); let mut span = MultiSpan::from_span(name_span);
span.push_span_label(iter_call.span, "the iterator could be used here instead"); span.push_span_label(iter_call.span, "the iterator could be used here instead");
span_lint_hir_and_then( span_lint_hir_and_then(
cx, cx,
super::NEEDLESS_COLLECT, super::NEEDLESS_COLLECT,
collect_expr.hir_id, collect_expr.hir_id,
span, span,
NEEDLESS_COLLECT_MSG, NEEDLESS_COLLECT_MSG,
|diag| { |diag| {
let iter_replacement = let iter_replacement =
format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx)); format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx));
diag.multipart_suggestion( diag.multipart_suggestion(
iter_call.get_suggestion_text(), iter_call.get_suggestion_text(),
vec![(l.span, String::new()), (iter_call.span, iter_replacement)], vec![(l.span, String::new()), (iter_call.span, iter_replacement)],
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
}, },
); );
} }
}, },
_ => (), _ => (),
}
} }
} }

View file

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::get_parent_expr; use clippy_utils::get_parent_expr;
use clippy_utils::ty::implements_trait; use clippy_utils::ty::implements_trait;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::print::with_forced_trimmed_paths;
@ -10,17 +10,71 @@ use rustc_span::{sym, Span};
use super::UNNECESSARY_FALLIBLE_CONVERSIONS; use super::UNNECESSARY_FALLIBLE_CONVERSIONS;
#[derive(Copy, Clone)]
enum SpansKind {
TraitFn { trait_span: Span, fn_span: Span },
Fn { fn_span: Span },
}
/// What function is being called and whether that call is written as a method call or a function /// What function is being called and whether that call is written as a method call or a function
/// call /// call
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
#[expect(clippy::enum_variant_names)] #[expect(clippy::enum_variant_names)]
enum FunctionKind { enum FunctionKind {
/// `T::try_from(U)` /// `T::try_from(U)`
TryFromFunction, TryFromFunction(Option<SpansKind>),
/// `t.try_into()` /// `t.try_into()`
TryIntoMethod, TryIntoMethod,
/// `U::try_into(t)` /// `U::try_into(t)`
TryIntoFunction, TryIntoFunction(Option<SpansKind>),
}
impl FunctionKind {
fn appl_sugg(&self, parent_unwrap_call: Option<Span>, primary_span: Span) -> (Applicability, Vec<(Span, String)>) {
let Some(unwrap_span) = parent_unwrap_call else {
return (Applicability::Unspecified, self.default_sugg(primary_span));
};
match &self {
FunctionKind::TryFromFunction(None) | FunctionKind::TryIntoFunction(None) => {
(Applicability::Unspecified, self.default_sugg(primary_span))
},
_ => (
Applicability::MachineApplicable,
self.machine_applicable_sugg(primary_span, unwrap_span),
),
}
}
fn default_sugg(&self, primary_span: Span) -> Vec<(Span, String)> {
let replacement = match *self {
FunctionKind::TryFromFunction(_) => "From::from",
FunctionKind::TryIntoFunction(_) => "Into::into",
FunctionKind::TryIntoMethod => "into",
};
vec![(primary_span, String::from(replacement))]
}
fn machine_applicable_sugg(&self, primary_span: Span, unwrap_span: Span) -> Vec<(Span, String)> {
let (trait_name, fn_name) = match self {
FunctionKind::TryFromFunction(_) => ("From".to_owned(), "from".to_owned()),
FunctionKind::TryIntoFunction(_) | FunctionKind::TryIntoMethod => ("Into".to_owned(), "into".to_owned()),
};
let mut sugg = match *self {
FunctionKind::TryFromFunction(Some(spans)) | FunctionKind::TryIntoFunction(Some(spans)) => match spans {
SpansKind::TraitFn { trait_span, fn_span } => vec![(trait_span, trait_name), (fn_span, fn_name)],
SpansKind::Fn { fn_span } => vec![(fn_span, fn_name)],
},
FunctionKind::TryIntoMethod => vec![(primary_span, fn_name)],
// Or the suggestion is not machine-applicable
_ => unreachable!(),
};
sugg.push((unwrap_span, String::new()));
sugg
}
} }
fn check<'tcx>( fn check<'tcx>(
@ -35,8 +89,8 @@ fn check<'tcx>(
&& self_ty != other_ty && self_ty != other_ty
&& let Some(self_ty) = self_ty.as_type() && let Some(self_ty) = self_ty.as_type()
&& let Some(from_into_trait) = cx.tcx.get_diagnostic_item(match kind { && let Some(from_into_trait) = cx.tcx.get_diagnostic_item(match kind {
FunctionKind::TryFromFunction => sym::From, FunctionKind::TryFromFunction(_) => sym::From,
FunctionKind::TryIntoMethod | FunctionKind::TryIntoFunction => sym::Into, FunctionKind::TryIntoMethod | FunctionKind::TryIntoFunction(_) => sym::Into,
}) })
// If `T: TryFrom<U>` and `T: From<U>` both exist, then that means that the `TryFrom` // If `T: TryFrom<U>` and `T: From<U>` both exist, then that means that the `TryFrom`
// _must_ be from the blanket impl and cannot have been manually implemented // _must_ be from the blanket impl and cannot have been manually implemented
@ -45,49 +99,37 @@ fn check<'tcx>(
&& implements_trait(cx, self_ty, from_into_trait, &[other_ty]) && implements_trait(cx, self_ty, from_into_trait, &[other_ty])
&& let Some(other_ty) = other_ty.as_type() && let Some(other_ty) = other_ty.as_type()
{ {
// Extend the span to include the unwrap/expect call:
// `foo.try_into().expect("..")`
// ^^^^^^^^^^^^^^^^^^^^^^^
//
// `try_into().unwrap()` specifically can be trivially replaced with just `into()`,
// so that can be machine-applicable
let parent_unwrap_call = get_parent_expr(cx, expr).and_then(|parent| { let parent_unwrap_call = get_parent_expr(cx, expr).and_then(|parent| {
if let ExprKind::MethodCall(path, .., span) = parent.kind if let ExprKind::MethodCall(path, .., span) = parent.kind
&& let sym::unwrap | sym::expect = path.ident.name && let sym::unwrap | sym::expect = path.ident.name
{ {
Some(span) // include `.` before `unwrap`/`expect`
Some(span.with_lo(expr.span.hi()))
} else { } else {
None None
} }
}); });
let (source_ty, target_ty, sugg, span, applicability) = match kind {
FunctionKind::TryIntoMethod if let Some(unwrap_span) = parent_unwrap_call => {
// Extend the span to include the unwrap/expect call:
// `foo.try_into().expect("..")`
// ^^^^^^^^^^^^^^^^^^^^^^^
//
// `try_into().unwrap()` specifically can be trivially replaced with just `into()`,
// so that can be machine-applicable
( // If there is an unwrap/expect call, extend the span to include the call
self_ty, let span = if let Some(unwrap_call) = parent_unwrap_call {
other_ty, primary_span.with_hi(unwrap_call.hi())
"into()", } else {
primary_span.with_hi(unwrap_span.hi()), primary_span
Applicability::MachineApplicable,
)
},
FunctionKind::TryFromFunction => (
other_ty,
self_ty,
"From::from",
primary_span,
Applicability::Unspecified,
),
FunctionKind::TryIntoFunction => (
self_ty,
other_ty,
"Into::into",
primary_span,
Applicability::Unspecified,
),
FunctionKind::TryIntoMethod => (self_ty, other_ty, "into", primary_span, Applicability::Unspecified),
}; };
let (source_ty, target_ty) = match kind {
FunctionKind::TryIntoMethod | FunctionKind::TryIntoFunction(_) => (self_ty, other_ty),
FunctionKind::TryFromFunction(_) => (other_ty, self_ty),
};
let (applicability, sugg) = kind.appl_sugg(parent_unwrap_call, primary_span);
span_lint_and_then( span_lint_and_then(
cx, cx,
UNNECESSARY_FALLIBLE_CONVERSIONS, UNNECESSARY_FALLIBLE_CONVERSIONS,
@ -97,7 +139,7 @@ fn check<'tcx>(
with_forced_trimmed_paths!({ with_forced_trimmed_paths!({
diag.note(format!("converting `{source_ty}` to `{target_ty}` cannot fail")); diag.note(format!("converting `{source_ty}` to `{target_ty}` cannot fail"));
}); });
diag.span_suggestion(span, "use", sugg, applicability); diag.multipart_suggestion("use", sugg, applicability);
}, },
); );
} }
@ -125,13 +167,30 @@ pub(super) fn check_function(cx: &LateContext<'_>, expr: &Expr<'_>, callee: &Exp
&& let Some(item_def_id) = cx.qpath_res(qpath, callee.hir_id).opt_def_id() && let Some(item_def_id) = cx.qpath_res(qpath, callee.hir_id).opt_def_id()
&& let Some(trait_def_id) = cx.tcx.trait_of_item(item_def_id) && let Some(trait_def_id) = cx.tcx.trait_of_item(item_def_id)
{ {
let qpath_spans = match qpath {
QPath::Resolved(_, path) => {
if let [trait_seg, fn_seg] = path.segments {
Some(SpansKind::TraitFn {
trait_span: trait_seg.ident.span,
fn_span: fn_seg.ident.span,
})
} else {
None
}
},
QPath::TypeRelative(_, seg) => Some(SpansKind::Fn {
fn_span: seg.ident.span,
}),
QPath::LangItem(_, _) => unreachable!("`TryFrom` and `TryInto` are not lang items"),
};
check( check(
cx, cx,
expr, expr,
cx.typeck_results().node_args(callee.hir_id), cx.typeck_results().node_args(callee.hir_id),
match cx.tcx.get_diagnostic_name(trait_def_id) { match cx.tcx.get_diagnostic_name(trait_def_id) {
Some(sym::TryFrom) => FunctionKind::TryFromFunction, Some(sym::TryFrom) => FunctionKind::TryFromFunction(qpath_spans),
Some(sym::TryInto) => FunctionKind::TryIntoFunction, Some(sym::TryInto) => FunctionKind::TryIntoFunction(qpath_spans),
_ => return, _ => return,
}, },
callee.span, callee.span,

View file

@ -5,7 +5,8 @@ use clippy_utils::ty::implements_trait;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
use rustc_lint::LateContext; 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::sym;
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use std::iter; use std::iter;

View file

@ -3,7 +3,9 @@ use super::unnecessary_iter_cloned::{self, is_into_iter};
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_lang_item, peel_mid_ty_refs}; use clippy_utils::ty::{
get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_mid_ty_refs,
};
use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::visitors::find_all_ret_expressions;
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty}; use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -16,7 +18,8 @@ use rustc_lint::LateContext;
use rustc_middle::mir::Mutability; use rustc_middle::mir::Mutability;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
use rustc_middle::ty::{ use rustc_middle::ty::{
self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ImplPolarity, ParamTy, ProjectionPredicate,
TraitPredicate, Ty,
}; };
use rustc_span::{sym, Symbol}; use rustc_span::{sym, Symbol};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@ -53,6 +56,8 @@ pub fn check<'tcx>(
} }
check_other_call_arg(cx, expr, method_name, receiver); check_other_call_arg(cx, expr, method_name, receiver);
} }
} else {
check_borrow_predicate(cx, expr);
} }
} }
@ -590,3 +595,92 @@ fn is_to_string_on_string_like<'a>(
false false
} }
} }
fn is_a_std_map_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
is_type_diagnostic_item(cx, ty, sym::HashSet)
|| is_type_diagnostic_item(cx, ty, sym::HashMap)
|| is_type_diagnostic_item(cx, ty, sym::BTreeMap)
|| is_type_diagnostic_item(cx, ty, sym::BTreeSet)
}
fn is_str_and_string(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool {
original_arg_ty.is_str() && is_type_lang_item(cx, arg_ty, LangItem::String)
}
fn is_slice_and_vec(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool {
(original_arg_ty.is_slice() || original_arg_ty.is_array() || original_arg_ty.is_array_slice())
&& is_type_diagnostic_item(cx, arg_ty, sym::Vec)
}
// This function will check the following:
// 1. The argument is a non-mutable reference.
// 2. It calls `to_owned()`, `to_string()` or `to_vec()`.
// 3. That the method is called on `String` or on `Vec` (only types supported for the moment).
fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx>) {
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, expr) = arg.kind
&& let ExprKind::MethodCall(method_path, caller, &[], _) = expr.kind
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
&& let method_name = method_path.ident.name.as_str()
&& match method_name {
"to_owned" => cx.tcx.is_diagnostic_item(sym::to_owned_method, method_def_id),
"to_string" => cx.tcx.is_diagnostic_item(sym::to_string_method, method_def_id),
"to_vec" => cx
.tcx
.impl_of_method(method_def_id)
.filter(|&impl_did| cx.tcx.type_of(impl_did).instantiate_identity().is_slice())
.is_some(),
_ => false,
}
&& let original_arg_ty = cx.typeck_results().node_type(caller.hir_id).peel_refs()
&& let arg_ty = cx.typeck_results().expr_ty(arg)
&& let ty::Ref(_, arg_ty, Mutability::Not) = arg_ty.kind()
// FIXME: try to fix `can_change_type` to make it work in this case.
// && can_change_type(cx, caller, *arg_ty)
&& let arg_ty = arg_ty.peel_refs()
// For now we limit this lint to `String` and `Vec`.
&& (is_str_and_string(cx, arg_ty, original_arg_ty) || is_slice_and_vec(cx, arg_ty, original_arg_ty))
&& let Some(snippet) = snippet_opt(cx, caller.span)
{
span_lint_and_sugg(
cx,
UNNECESSARY_TO_OWNED,
arg.span,
&format!("unnecessary use of `{method_name}`"),
"replace it with",
if original_arg_ty.is_array() {
format!("{snippet}.as_slice()")
} else {
snippet
},
Applicability::MaybeIncorrect,
);
}
}
// In std "map types", the getters all expect a `Borrow<Key>` generic argument. So in here, we
// check that:
// 1. This is a method with only one argument that doesn't come from a trait.
// 2. That it has `Borrow` in its generic predicates.
// 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, BTreeSet`, `BTreeMap`).
fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if let ExprKind::MethodCall(_, caller, &[arg], _) = expr.kind
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
&& cx.tcx.trait_of_item(method_def_id).is_none()
&& let Some(borrow_id) = cx.tcx.get_diagnostic_item(sym::Borrow)
&& cx.tcx.predicates_of(method_def_id).predicates.iter().any(|(pred, _)| {
if let ClauseKind::Trait(trait_pred) = pred.kind().skip_binder()
&& trait_pred.polarity == ImplPolarity::Positive
&& trait_pred.trait_ref.def_id == borrow_id
{
true
} else {
false
}
})
&& let caller_ty = cx.typeck_results().expr_ty(caller)
// For now we limit it to "map types".
&& is_a_std_map_type(cx, caller_ty)
{
check_if_applicable_to_argument(cx, &arg);
}
}

View file

@ -2,8 +2,8 @@ use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_from_proc_macro; use clippy_utils::is_from_proc_macro;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_item, Visitor}; use rustc_hir::intravisit::{walk_item, walk_trait_item, Visitor};
use rustc_hir::{GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind}; use rustc_hir::{GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, TraitItem, UsePath};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
@ -53,7 +53,7 @@ impl MinIdentChars {
&& str.len() <= self.min_ident_chars_threshold as usize && str.len() <= self.min_ident_chars_threshold as usize
&& !str.starts_with('_') && !str.starts_with('_')
&& !str.is_empty() && !str.is_empty()
&& self.allowed_idents_below_min_chars.get(&str.to_owned()).is_none() && !self.allowed_idents_below_min_chars.contains(str)
} }
} }
@ -66,6 +66,14 @@ impl LateLintPass<'_> for MinIdentChars {
walk_item(&mut IdentVisitor { conf: self, cx }, item); walk_item(&mut IdentVisitor { conf: self, cx }, item);
} }
fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
if self.min_ident_chars_threshold == 0 {
return;
}
walk_trait_item(&mut IdentVisitor { conf: self, cx }, item);
}
// This is necessary as `Node::Pat`s are not visited in `visit_id`. :/ // This is necessary as `Node::Pat`s are not visited in `visit_id`. :/
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
if let PatKind::Binding(_, _, ident, ..) = pat.kind if let PatKind::Binding(_, _, ident, ..) = pat.kind
@ -105,11 +113,26 @@ impl Visitor<'_> for IdentVisitor<'_, '_> {
let str = ident.as_str(); let str = ident.as_str();
if conf.is_ident_too_short(cx, str, ident.span) { if conf.is_ident_too_short(cx, str, ident.span) {
if let Node::Item(item) = node // Check whether the node is part of a `use` statement. We don't want to emit a warning if the user
&& let ItemKind::Use(..) = item.kind // has no control over the type.
let usenode = opt_as_use_node(node).or_else(|| {
cx.tcx
.hir()
.parent_iter(hir_id)
.find_map(|(_, node)| opt_as_use_node(node))
});
// If the name of the identifier is the same as the one of the imported item, this means that we
// found a `use foo::bar`. We can early-return to not emit the warning.
// If however the identifier is different, this means it is an alias (`use foo::bar as baz`). In
// this case, we need to emit the warning for `baz`.
if let Some(imported_item_path) = usenode
&& let Some(Res::Def(_, imported_item_defid)) = imported_item_path.res.first()
&& cx.tcx.item_name(*imported_item_defid).as_str() == str
{ {
return; return;
} }
// `struct Awa<T>(T)` // `struct Awa<T>(T)`
// ^ // ^
if let Node::PathSegment(path) = node { if let Node::PathSegment(path) = node {
@ -160,3 +183,16 @@ fn emit_min_ident_chars(conf: &MinIdentChars, cx: &impl LintContext, ident: &str
}; };
span_lint(cx, MIN_IDENT_CHARS, span, &help); span_lint(cx, MIN_IDENT_CHARS, span, &help);
} }
/// Attempt to convert the node to an [`ItemKind::Use`] node.
///
/// If it is, return the [`UsePath`] contained within.
fn opt_as_use_node(node: Node<'_>) -> Option<&'_ UsePath<'_>> {
if let Node::Item(item) = node
&& let ItemKind::Use(path, _) = item.kind
{
Some(path)
} else {
None
}
}

View file

@ -96,10 +96,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
self.found = true; self.found = true;
return; return;
}, },
ExprKind::If(..) => {
self.found = true;
return;
},
ExprKind::Path(_) => { ExprKind::Path(_) => {
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) { if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
if adj if adj

View file

@ -139,7 +139,7 @@ fn condition_needs_parentheses(e: &Expr<'_>) -> bool {
fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool { fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
matches!( matches!(
get_parent_node(cx.tcx, id), get_parent_node(cx.tcx, id),
Some(Node::Stmt(..) | Node::Block(Block { stmts: &[], .. })) Node::Stmt(..) | Node::Block(Block { stmts: &[], .. })
) )
} }

View file

@ -236,11 +236,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
// #11182; do not lint if mutability is required elsewhere // #11182; do not lint if mutability is required elsewhere
if let ExprKind::Path(..) = expr.kind if let ExprKind::Path(..) = expr.kind
&& let Some(parent) = get_parent_node(cx.tcx, expr.hir_id)
&& let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(expr).kind() && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(expr).kind()
&& let Some(def_id) = def_id.as_local() && let Some(def_id) = def_id.as_local()
{ {
if let Node::Expr(e) = parent if let Node::Expr(e) = get_parent_node(cx.tcx, expr.hir_id)
&& let ExprKind::Call(call, _) = e.kind && let ExprKind::Call(call, _) = e.kind
&& call.hir_id == expr.hir_id && call.hir_id == expr.hir_id
{ {

View file

@ -75,10 +75,6 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind { if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
let name = impl_item.ident.name; let name = impl_item.ident.name;
let id = impl_item.owner_id; let id = impl_item.owner_id;
if sig.header.constness == hir::Constness::Const {
// can't be implemented by default
return;
}
if sig.header.unsafety == hir::Unsafety::Unsafe { if sig.header.unsafety == hir::Unsafety::Unsafe {
// can't be implemented for unsafe new // can't be implemented for unsafe new
return; return;

View file

@ -5,8 +5,8 @@ use clippy_utils::{any_parent_is_automatically_derived, get_parent_node, is_lint
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::{ use rustc_hir::{
is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, Node, PatKind, Stmt, is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind,
StmtKind, UnsafeSource, Stmt, StmtKind, UnsafeSource,
}; };
use rustc_infer::infer::TyCtxtInferExt as _; use rustc_infer::infer::TyCtxtInferExt as _;
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -43,10 +43,6 @@ declare_clippy_lint! {
/// executed. However, as they have no effect and shouldn't be used further on, all they /// executed. However, as they have no effect and shouldn't be used further on, all they
/// do is make the code less readable. /// do is make the code less readable.
/// ///
/// ### Known problems
/// Further usage of this variable is not checked, which can lead to false positives if it is
/// used later in the code.
///
/// ### Example /// ### Example
/// ```rust,ignore /// ```rust,ignore
/// let _i_serve_no_purpose = 1; /// let _i_serve_no_purpose = 1;
@ -144,7 +140,7 @@ impl NoEffect {
for parent in cx.tcx.hir().parent_iter(stmt.hir_id) { for parent in cx.tcx.hir().parent_iter(stmt.hir_id) {
if let Node::Item(item) = parent.1 if let Node::Item(item) = parent.1
&& let ItemKind::Fn(..) = item.kind && let ItemKind::Fn(..) = item.kind
&& let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) && let Node::Block(block) = get_parent_node(cx.tcx, stmt.hir_id)
&& let [.., final_stmt] = block.stmts && let [.., final_stmt] = block.stmts
&& final_stmt.hir_id == stmt.hir_id && final_stmt.hir_id == stmt.hir_id
{ {
@ -180,6 +176,7 @@ impl NoEffect {
} }
} else if let StmtKind::Local(local) = stmt.kind { } else if let StmtKind::Local(local) = stmt.kind {
if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id) if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id)
&& !matches!(local.source, LocalSource::AsyncFn)
&& let Some(init) = local.init && let Some(init) = local.init
&& local.els.is_none() && local.els.is_none()
&& !local.pat.span.from_expansion() && !local.pat.span.from_expansion()

View file

@ -112,7 +112,7 @@ declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL
impl LateLintPass<'_> for NonCanonicalImpls { impl LateLintPass<'_> for NonCanonicalImpls {
#[expect(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
let Some(Node::Item(item)) = get_parent_node(cx.tcx, impl_item.hir_id()) else { let Node::Item(item) = get_parent_node(cx.tcx, impl_item.hir_id()) else {
return; return;
}; };
let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) else { let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) else {

View file

@ -119,7 +119,6 @@ impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
// this list contains lists of names that are allowed to be similar // this list contains lists of names that are allowed to be similar
// the assumption is that no name is ever contained in multiple lists. // the assumption is that no name is ever contained in multiple lists.
#[rustfmt::skip]
const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[ const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[
&["parsed", "parser"], &["parsed", "parser"],
&["lhs", "rhs"], &["lhs", "rhs"],
@ -132,6 +131,14 @@ const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[
&["iter", "item"], &["iter", "item"],
]; ];
/// Characters that look visually similar
const SIMILAR_CHARS: &[(char, char)] = &[('l', 'i'), ('l', '1'), ('i', '1'), ('u', 'v')];
/// Return true if two characters are visually similar
fn chars_are_similar(a: char, b: char) -> bool {
a == b || SIMILAR_CHARS.contains(&(a, b)) || SIMILAR_CHARS.contains(&(b, a))
}
struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>); struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);
impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> { impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
@ -189,7 +196,6 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
} }
} }
#[expect(clippy::too_many_lines)]
fn check_ident(&mut self, ident: Ident) { fn check_ident(&mut self, ident: Ident) {
let interned_name = ident.name.as_str(); let interned_name = ident.name.as_str();
if interned_name.chars().any(char::is_uppercase) { if interned_name.chars().any(char::is_uppercase) {
@ -219,71 +225,28 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
if allowed_to_be_similar(interned_name, existing_name.exemptions) { if allowed_to_be_similar(interned_name, existing_name.exemptions) {
continue; continue;
} }
match existing_name.len.cmp(&count) {
Ordering::Greater => {
if existing_name.len - count != 1
|| levenstein_not_1(interned_name, existing_name.interned.as_str())
{
continue;
}
},
Ordering::Less => {
if count - existing_name.len != 1
|| levenstein_not_1(existing_name.interned.as_str(), interned_name)
{
continue;
}
},
Ordering::Equal => {
let mut interned_chars = interned_name.chars();
let interned_str = existing_name.interned.as_str();
let mut existing_chars = interned_str.chars();
let first_i = interned_chars.next().expect("we know we have at least one char");
let first_e = existing_chars.next().expect("we know we have at least one char");
let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();
if eq_or_numeric((first_i, first_e)) { let existing_str = existing_name.interned.as_str();
let last_i = interned_chars.next_back().expect("we know we have at least two chars");
let last_e = existing_chars.next_back().expect("we know we have at least two chars"); // The first char being different is usually enough to set identifiers apart, as long
if eq_or_numeric((last_i, last_e)) { // as the characters aren't too similar.
if interned_chars if !chars_are_similar(
.zip(existing_chars) interned_name.chars().next().expect("len >= 1"),
.filter(|&ie| !eq_or_numeric(ie)) existing_str.chars().next().expect("len >= 1"),
.count() ) {
!= 1 continue;
{
continue;
}
} else {
let second_last_i = interned_chars
.next_back()
.expect("we know we have at least three chars");
let second_last_e = existing_chars
.next_back()
.expect("we know we have at least three chars");
if !eq_or_numeric((second_last_i, second_last_e))
|| second_last_i == '_'
|| !interned_chars.zip(existing_chars).all(eq_or_numeric)
{
// allowed similarity foo_x, foo_y
// or too many chars differ (foo_x, boo_y) or (foox, booy)
continue;
}
}
} else {
let second_i = interned_chars.next().expect("we know we have at least two chars");
let second_e = existing_chars.next().expect("we know we have at least two chars");
if !eq_or_numeric((second_i, second_e))
|| second_i == '_'
|| !interned_chars.zip(existing_chars).all(eq_or_numeric)
{
// allowed similarity x_foo, y_foo
// or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
continue;
}
}
},
} }
let dissimilar = match existing_name.len.cmp(&count) {
Ordering::Greater => existing_name.len - count != 1 || levenstein_not_1(interned_name, existing_str),
Ordering::Less => count - existing_name.len != 1 || levenstein_not_1(existing_str, interned_name),
Ordering::Equal => Self::equal_length_strs_not_similar(interned_name, existing_str),
};
if dissimilar {
continue;
}
span_lint_and_then( span_lint_and_then(
self.0.cx, self.0.cx,
SIMILAR_NAMES, SIMILAR_NAMES,
@ -302,6 +265,57 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
len: count, len: count,
}); });
} }
fn equal_length_strs_not_similar(interned_name: &str, existing_name: &str) -> bool {
let mut interned_chars = interned_name.chars();
let mut existing_chars = existing_name.chars();
let first_i = interned_chars.next().expect("we know we have at least one char");
let first_e = existing_chars.next().expect("we know we have at least one char");
let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();
if eq_or_numeric((first_i, first_e)) {
let last_i = interned_chars.next_back().expect("we know we have at least two chars");
let last_e = existing_chars.next_back().expect("we know we have at least two chars");
if eq_or_numeric((last_i, last_e)) {
if interned_chars
.zip(existing_chars)
.filter(|&ie| !eq_or_numeric(ie))
.count()
!= 1
{
return true;
}
} else {
let second_last_i = interned_chars
.next_back()
.expect("we know we have at least three chars");
let second_last_e = existing_chars
.next_back()
.expect("we know we have at least three chars");
if !eq_or_numeric((second_last_i, second_last_e))
|| second_last_i == '_'
|| !interned_chars.zip(existing_chars).all(eq_or_numeric)
{
// allowed similarity foo_x, foo_y
// or too many chars differ (foo_x, boo_y) or (foox, booy)
return true;
}
}
} else {
let second_i = interned_chars.next().expect("we know we have at least two chars");
let second_e = existing_chars.next().expect("we know we have at least two chars");
if !eq_or_numeric((second_i, second_e))
|| second_i == '_'
|| !interned_chars.zip(existing_chars).all(eq_or_numeric)
{
// allowed similarity x_foo, y_foo
// or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
return true;
}
}
false
}
} }
impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> { impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {

View file

@ -228,23 +228,23 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
// `skip_params` is either `0` or `1` to skip the `self` parameter in trait functions. // `skip_params` is either `0` or `1` to skip the `self` parameter in trait functions.
// It can't be renamed, and it can't be removed without removing it from multiple functions. // It can't be renamed, and it can't be removed without removing it from multiple functions.
let (fn_id, fn_kind, skip_params) = match get_parent_node(cx.tcx, body.value.hir_id) { let (fn_id, fn_kind, skip_params) = match get_parent_node(cx.tcx, body.value.hir_id) {
Some(Node::Item(i)) => (i.owner_id.to_def_id(), FnKind::Fn, 0), Node::Item(i) => (i.owner_id.to_def_id(), FnKind::Fn, 0),
Some(Node::TraitItem(&TraitItem { Node::TraitItem(&TraitItem {
kind: TraitItemKind::Fn(ref sig, _), kind: TraitItemKind::Fn(ref sig, _),
owner_id, owner_id,
.. ..
})) => ( }) => (
owner_id.to_def_id(), owner_id.to_def_id(),
FnKind::TraitFn, FnKind::TraitFn,
usize::from(sig.decl.implicit_self.has_implicit_self()), usize::from(sig.decl.implicit_self.has_implicit_self()),
), ),
Some(Node::ImplItem(&ImplItem { Node::ImplItem(&ImplItem {
kind: ImplItemKind::Fn(ref sig, _), kind: ImplItemKind::Fn(ref sig, _),
owner_id, owner_id,
.. ..
})) => { }) => {
#[allow(trivial_casts)] #[allow(trivial_casts)]
if let Some(Node::Item(item)) = get_parent_node(cx.tcx, owner_id.into()) if let Node::Item(item) = get_parent_node(cx.tcx, owner_id.into())
&& let Some(trait_ref) = cx && let Some(trait_ref) = cx
.tcx .tcx
.impl_trait_ref(item.owner_id) .impl_trait_ref(item.owner_id)

View file

@ -8,7 +8,6 @@ use rustc_span::Span;
use super::DOUBLE_COMPARISONS; use super::DOUBLE_COMPARISONS;
#[expect(clippy::similar_names)]
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) { pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) {
let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) { let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) {
(ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => { (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => {

View file

@ -11,7 +11,7 @@ use rustc_middle::ty::{self, Ty};
use super::OP_REF; use super::OP_REF;
#[expect(clippy::similar_names, clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
pub(crate) fn check<'tcx>( pub(crate) fn check<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
e: &'tcx Expr<'_>, e: &'tcx Expr<'_>,

View file

@ -1,6 +1,6 @@
use clippy_config::types::PubUnderscoreFieldsBehaviour; use clippy_config::types::PubUnderscoreFieldsBehaviour;
use clippy_utils::attrs::is_doc_hidden; use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::is_path_lang_item; use clippy_utils::is_path_lang_item;
use rustc_hir::{FieldDef, Item, ItemKind, LangItem}; use rustc_hir::{FieldDef, Item, ItemKind, LangItem};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -69,13 +69,15 @@ impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields {
// We ignore fields that are `PhantomData`. // We ignore fields that are `PhantomData`.
&& !is_path_lang_item(cx, field.ty, LangItem::PhantomData) && !is_path_lang_item(cx, field.ty, LangItem::PhantomData)
{ {
span_lint_and_help( span_lint_hir_and_then(
cx, cx,
PUB_UNDERSCORE_FIELDS, PUB_UNDERSCORE_FIELDS,
field.hir_id,
field.vis_span.to(field.ident.span), field.vis_span.to(field.ident.span),
"field marked as public but also inferred as unused because it's prefixed with `_`", "field marked as public but also inferred as unused because it's prefixed with `_`",
None, |diag| {
"consider removing the underscore, or making the field private", diag.help("consider removing the underscore, or making the field private");
},
); );
} }
} }

View file

@ -289,7 +289,7 @@ impl QuestionMark {
{ {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_))); let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Node::Stmt(_));
let sugg = format!( let sugg = format!(
"{receiver_str}{}?{}", "{receiver_str}{}?{}",
if by_ref == ByRef::Yes { ".as_ref()" } else { "" }, if by_ref == ByRef::Yes { ".as_ref()" } else { "" },

View file

@ -1,7 +1,7 @@
use crate::rustc_lint::LintContext; use crate::rustc_lint::LintContext;
use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::get_parent_expr;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::{get_parent_expr, get_parent_node};
use hir::Param; use hir::Param;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
@ -200,11 +200,11 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
hint = hint.asyncify(); hint = hint.asyncify();
} }
let is_in_fn_call_arg = let is_in_fn_call_arg = if let Node::Expr(expr) = get_parent_node(cx.tcx, expr.hir_id) {
clippy_utils::get_parent_node(cx.tcx, expr.hir_id).is_some_and(|x| match x { matches!(expr.kind, hir::ExprKind::Call(_, _))
Node::Expr(expr) => matches!(expr.kind, hir::ExprKind::Call(_, _)), } else {
_ => false, false
}); };
// avoid clippy::double_parens // avoid clippy::double_parens
if !is_in_fn_call_arg { if !is_in_fn_call_arg {

View file

@ -47,7 +47,6 @@ struct ExistingName {
} }
impl<'tcx> LateLintPass<'tcx> for SameNameMethod { impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
#[expect(clippy::too_many_lines)]
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
let mut map = FxHashMap::<Res, ExistingName>::default(); let mut map = FxHashMap::<Res, ExistingName>::default();
@ -75,24 +74,23 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
match of_trait { match of_trait {
Some(trait_ref) => { Some(trait_ref) => {
let mut methods_in_trait: BTreeSet<Symbol> = let mut methods_in_trait: BTreeSet<Symbol> = if let Node::TraitRef(TraitRef { path, .. }) =
if let Node::TraitRef(TraitRef { path, .. }) = cx.tcx.hir_node(trait_ref.hir_ref_id)
cx.tcx.hir_node(trait_ref.hir_ref_id) && let Res::Def(DefKind::Trait, did) = path.res
&& let Res::Def(DefKind::Trait, did) = path.res {
{ // FIXME: if
// FIXME: if // `rustc_middle::ty::assoc::AssocItems::items` is public,
// `rustc_middle::ty::assoc::AssocItems::items` is public, // we can iterate its keys instead of `in_definition_order`,
// we can iterate its keys instead of `in_definition_order`, // which's more efficient
// which's more efficient cx.tcx
cx.tcx .associated_items(did)
.associated_items(did) .in_definition_order()
.in_definition_order() .filter(|assoc_item| matches!(assoc_item.kind, AssocKind::Fn))
.filter(|assoc_item| matches!(assoc_item.kind, AssocKind::Fn)) .map(|assoc_item| assoc_item.name)
.map(|assoc_item| assoc_item.name) .collect()
.collect() } else {
} else { BTreeSet::new()
BTreeSet::new() };
};
let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| { let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
if let Some((impl_span, hir_id)) = existing_name.impl_methods.get(&method_name) { if let Some((impl_span, hir_id)) = existing_name.impl_methods.get(&method_name) {

View file

@ -51,11 +51,11 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings {
{ {
let ctxt = expr.span.ctxt(); let ctxt = expr.span.ctxt();
let span = match get_parent_node(cx.tcx, expr.hir_id) { let span = match get_parent_node(cx.tcx, expr.hir_id) {
Some(Node::Block(&Block { Node::Block(&Block {
rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
span, span,
.. ..
})) if span.ctxt() == ctxt && !is_expr_unsafe(cx, self_arg) => span, }) if span.ctxt() == ctxt && !is_expr_unsafe(cx, self_arg) => span,
_ => expr.span, _ => expr.span,
}; };

View file

@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::implements_trait;
use rustc_hir::{Impl, Item, ItemKind}; use rustc_hir::{Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
@ -53,6 +54,8 @@ impl<'tcx> LateLintPass<'tcx> for ToStringTraitImpl {
}) = it.kind }) = it.kind
&& let Some(trait_did) = trait_ref.trait_def_id() && let Some(trait_did) = trait_ref.trait_def_id()
&& cx.tcx.is_diagnostic_item(sym::ToString, trait_did) && cx.tcx.is_diagnostic_item(sym::ToString, trait_did)
&& let Some(display_did) = cx.tcx.get_diagnostic_item(sym::Display)
&& !implements_trait(cx, cx.tcx.type_of(it.owner_id).instantiate_identity(), display_did, &[])
{ {
span_lint_and_help( span_lint_and_help(
cx, cx,

View file

@ -153,10 +153,7 @@ fn all_bindings_are_for_conv<'tcx>(
let Some(locals) = locals.iter().map(|e| path_to_local(e)).collect::<Option<Vec<_>>>() else { let Some(locals) = locals.iter().map(|e| path_to_local(e)).collect::<Option<Vec<_>>>() else {
return false; return false;
}; };
let local_parents = locals let local_parents = locals.iter().map(|l| cx.tcx.parent_hir_node(*l)).collect::<Vec<_>>();
.iter()
.map(|l| cx.tcx.parent_hir_node(*l))
.collect::<Vec<_>>();
local_parents local_parents
.iter() .iter()

View file

@ -340,47 +340,43 @@ fn block_parents_have_safety_comment(
cx: &LateContext<'_>, cx: &LateContext<'_>,
id: hir::HirId, id: hir::HirId,
) -> bool { ) -> bool {
if let Some(node) = get_parent_node(cx.tcx, id) { let (span, hir_id) = match get_parent_node(cx.tcx, id) {
let (span, hir_id) = match node { Node::Expr(expr) => match get_parent_node(cx.tcx, expr.hir_id) {
Node::Expr(expr) => match get_parent_node(cx.tcx, expr.hir_id) { Node::Local(hir::Local { span, hir_id, .. }) => (*span, *hir_id),
Some(Node::Local(hir::Local { span, hir_id, .. })) => (*span, *hir_id),
Some(Node::Item(hir::Item {
kind: hir::ItemKind::Const(..) | ItemKind::Static(..),
span,
owner_id,
..
})) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)),
_ => {
if is_branchy(expr) {
return false;
}
(expr.span, expr.hir_id)
},
},
Node::Stmt(hir::Stmt {
kind:
hir::StmtKind::Local(hir::Local { span, hir_id, .. })
| hir::StmtKind::Expr(hir::Expr { span, hir_id, .. })
| hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }),
..
})
| Node::Local(hir::Local { span, hir_id, .. }) => (*span, *hir_id),
Node::Item(hir::Item { Node::Item(hir::Item {
kind: hir::ItemKind::Const(..) | ItemKind::Static(..), kind: hir::ItemKind::Const(..) | ItemKind::Static(..),
span, span,
owner_id, owner_id,
.. ..
}) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)), }) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)),
_ => return false, _ => {
}; if is_branchy(expr) {
// if unsafe block is part of a let/const/static statement, return false;
// and accept_comment_above_statement is set to true }
// we accept the safety comment in the line the precedes this statement. (expr.span, expr.hir_id)
accept_comment_above_statement },
&& span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attributes) },
} else { Node::Stmt(hir::Stmt {
false kind:
} hir::StmtKind::Local(hir::Local { span, hir_id, .. })
| hir::StmtKind::Expr(hir::Expr { span, hir_id, .. })
| hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }),
..
})
| Node::Local(hir::Local { span, hir_id, .. }) => (*span, *hir_id),
Node::Item(hir::Item {
kind: hir::ItemKind::Const(..) | ItemKind::Static(..),
span,
owner_id,
..
}) => (*span, cx.tcx.local_def_id_to_hir_id(owner_id.def_id)),
_ => return false,
};
// if unsafe block is part of a let/const/static statement,
// and accept_comment_above_statement is set to true
// we accept the safety comment in the line the precedes this statement.
accept_comment_above_statement
&& span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attributes)
} }
/// Extends `span` to also include its attributes, then checks if that span has a safety comment. /// Extends `span` to also include its attributes, then checks if that span has a safety comment.
@ -449,53 +445,49 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf
if item.span.ctxt() != SyntaxContext::root() { if item.span.ctxt() != SyntaxContext::root() {
return HasSafetyComment::No; return HasSafetyComment::No;
} }
if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { let comment_start = match get_parent_node(cx.tcx, item.hir_id()) {
let comment_start = match parent_node { Node::Crate(parent_mod) => comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item),
Node::Crate(parent_mod) => { Node::Item(parent_item) => {
comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) if let ItemKind::Mod(parent_mod) = &parent_item.kind {
}, comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item)
Node::Item(parent_item) => { } else {
if let ItemKind::Mod(parent_mod) = &parent_item.kind {
comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item)
} else {
// Doesn't support impls in this position. Pretend a comment was found.
return HasSafetyComment::Maybe;
}
},
Node::Stmt(stmt) => {
if let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) {
walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo)
} else {
// Problem getting the parent node. Pretend a comment was found.
return HasSafetyComment::Maybe;
}
},
_ => {
// Doesn't support impls in this position. Pretend a comment was found. // Doesn't support impls in this position. Pretend a comment was found.
return HasSafetyComment::Maybe; return HasSafetyComment::Maybe;
}, }
}; },
Node::Stmt(stmt) => {
let source_map = cx.sess().source_map(); if let Node::Block(block) = get_parent_node(cx.tcx, stmt.hir_id) {
if let Some(comment_start) = comment_start walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo)
&& let Ok(unsafe_line) = source_map.lookup_line(item.span.lo())
&& let Ok(comment_start_line) = source_map.lookup_line(comment_start)
&& Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
&& let Some(src) = unsafe_line.sf.src.as_deref()
{
return if comment_start_line.line >= unsafe_line.line {
HasSafetyComment::No
} else { } else {
match text_has_safety_comment( // Problem getting the parent node. Pretend a comment was found.
src, return HasSafetyComment::Maybe;
&unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line], }
unsafe_line.sf.start_pos, },
) { _ => {
Some(b) => HasSafetyComment::Yes(b), // Doesn't support impls in this position. Pretend a comment was found.
None => HasSafetyComment::No, return HasSafetyComment::Maybe;
} },
}; };
}
let source_map = cx.sess().source_map();
if let Some(comment_start) = comment_start
&& let Ok(unsafe_line) = source_map.lookup_line(item.span.lo())
&& let Ok(comment_start_line) = source_map.lookup_line(comment_start)
&& Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
&& let Some(src) = unsafe_line.sf.src.as_deref()
{
return if comment_start_line.line >= unsafe_line.line {
HasSafetyComment::No
} else {
match text_has_safety_comment(
src,
&unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line],
unsafe_line.sf.start_pos,
) {
Some(b) => HasSafetyComment::Yes(b),
None => HasSafetyComment::No,
}
};
} }
HasSafetyComment::Maybe HasSafetyComment::Maybe
} }
@ -512,32 +504,30 @@ fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> H
return HasSafetyComment::No; return HasSafetyComment::No;
} }
if let Some(parent_node) = get_parent_node(cx.tcx, hir_id) { let comment_start = match get_parent_node(cx.tcx, hir_id) {
let comment_start = match parent_node { Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo),
Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), _ => return HasSafetyComment::Maybe,
_ => return HasSafetyComment::Maybe, };
};
let source_map = cx.sess().source_map(); let source_map = cx.sess().source_map();
if let Some(comment_start) = comment_start if let Some(comment_start) = comment_start
&& let Ok(unsafe_line) = source_map.lookup_line(span.lo()) && let Ok(unsafe_line) = source_map.lookup_line(span.lo())
&& let Ok(comment_start_line) = source_map.lookup_line(comment_start) && let Ok(comment_start_line) = source_map.lookup_line(comment_start)
&& Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
&& let Some(src) = unsafe_line.sf.src.as_deref() && let Some(src) = unsafe_line.sf.src.as_deref()
{ {
return if comment_start_line.line >= unsafe_line.line { return if comment_start_line.line >= unsafe_line.line {
HasSafetyComment::No HasSafetyComment::No
} else { } else {
match text_has_safety_comment( match text_has_safety_comment(
src, src,
&unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line], &unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line],
unsafe_line.sf.start_pos, unsafe_line.sf.start_pos,
) { ) {
Some(b) => HasSafetyComment::Yes(b), Some(b) => HasSafetyComment::Yes(b),
None => HasSafetyComment::No, None => HasSafetyComment::No,
} }
}; };
}
} }
HasSafetyComment::Maybe HasSafetyComment::Maybe
} }

View file

@ -103,7 +103,7 @@ fn expr_needs_inferred_result<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -
return false; return false;
} }
while let Some(id) = locals_to_check.pop() { while let Some(id) = locals_to_check.pop() {
if let Some(Node::Local(l)) = get_parent_node(cx.tcx, id) { if let Node::Local(l) = get_parent_node(cx.tcx, id) {
if !l.ty.map_or(true, |ty| matches!(ty.kind, TyKind::Infer)) { if !l.ty.map_or(true, |ty| matches!(ty.kind, TyKind::Infer)) {
return false; return false;
} }

View file

@ -1005,8 +1005,6 @@ fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -
} }
fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> {
let map = cx.tcx.hir();
match cx.tcx.parent_hir_node(hir_id) { match cx.tcx.parent_hir_node(hir_id) {
hir::Node::Local(local) => Some(local), hir::Node::Local(local) => Some(local),
hir::Node::Pat(pattern) => get_parent_local_hir_id(cx, pattern.hir_id), hir::Node::Pat(pattern) => get_parent_local_hir_id(cx, pattern.hir_id),

View file

@ -2,7 +2,7 @@
//! //!
//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s. //! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s.
#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] #![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
use crate::{both, over}; use crate::{both, over};
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
@ -296,7 +296,7 @@ pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> b
eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind)
} }
#[expect(clippy::too_many_lines)] // Just a big match statement #[expect(clippy::similar_names, clippy::too_many_lines)] // Just a big match statement
pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
use ItemKind::*; use ItemKind::*;
match (l, r) { match (l, r) {
@ -690,7 +690,9 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
match (&l.kind, &r.kind) { match (&l.kind, &r.kind) {
(Paren(l), _) => eq_ty(l, r), (Paren(l), _) => eq_ty(l, r),
(_, Paren(r)) => eq_ty(l, r), (_, Paren(r)) => eq_ty(l, r),
(Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err(_), Err(_)) | (CVarArgs, CVarArgs) => true, (Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err(_), Err(_)) | (CVarArgs, CVarArgs) => {
true
},
(Slice(l), Slice(r)) => eq_ty(l, r), (Slice(l), Slice(r)) => eq_ty(l, r),
(Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value), (Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value),
(Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty), (Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty),

View file

@ -40,7 +40,7 @@ fn docs_link(diag: &mut DiagnosticBuilder<'_, ()>, lint: &'static Lint) {
/// ///
/// ```ignore /// ```ignore
/// error: usage of mem::forget on Drop type /// error: usage of mem::forget on Drop type
/// --> $DIR/mem_forget.rs:17:5 /// --> tests/ui/mem_forget.rs:17:5
/// | /// |
/// 17 | std::mem::forget(seven); /// 17 | std::mem::forget(seven);
/// | ^^^^^^^^^^^^^^^^^^^^^^^ /// | ^^^^^^^^^^^^^^^^^^^^^^^
@ -65,7 +65,7 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
/// ///
/// ```text /// ```text
/// error: constant division of 0.0 with 0.0 will always result in NaN /// error: constant division of 0.0 with 0.0 will always result in NaN
/// --> $DIR/zero_div_zero.rs:6:25 /// --> tests/ui/zero_div_zero.rs:6:25
/// | /// |
/// 6 | let other_f64_nan = 0.0f64 / 0.0; /// 6 | let other_f64_nan = 0.0f64 / 0.0;
/// | ^^^^^^^^^^^^ /// | ^^^^^^^^^^^^
@ -103,14 +103,14 @@ pub fn span_lint_and_help<T: LintContext>(
/// ///
/// ```text /// ```text
/// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. /// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
/// --> $DIR/drop_forget_ref.rs:10:5 /// --> tests/ui/drop_forget_ref.rs:10:5
/// | /// |
/// 10 | forget(&SomeStruct); /// 10 | forget(&SomeStruct);
/// | ^^^^^^^^^^^^^^^^^^^ /// | ^^^^^^^^^^^^^^^^^^^
/// | /// |
/// = note: `-D clippy::forget-ref` implied by `-D warnings` /// = note: `-D clippy::forget-ref` implied by `-D warnings`
/// note: argument has type &SomeStruct /// note: argument has type &SomeStruct
/// --> $DIR/drop_forget_ref.rs:10:12 /// --> tests/ui/drop_forget_ref.rs:10:12
/// | /// |
/// 10 | forget(&SomeStruct); /// 10 | forget(&SomeStruct);
/// | ^^^^^^^^^^^ /// | ^^^^^^^^^^^
@ -186,7 +186,7 @@ pub fn span_lint_hir_and_then(
/// ///
/// ```text /// ```text
/// error: This `.fold` can be more succinctly expressed as `.any` /// error: This `.fold` can be more succinctly expressed as `.any`
/// --> $DIR/methods.rs:390:13 /// --> tests/ui/methods.rs:390:13
/// | /// |
/// 390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2); /// 390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2);
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)` /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`

View file

@ -132,7 +132,6 @@ impl HirEqInterExpr<'_, '_, '_> {
} }
/// Checks whether two blocks are the same. /// Checks whether two blocks are the same.
#[expect(clippy::similar_names)]
fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
use TokenKind::{Semi, Whitespace}; use TokenKind::{Semi, Whitespace};
if left.stmts.len() != right.stmts.len() { if left.stmts.len() != right.stmts.len() {
@ -247,7 +246,7 @@ impl HirEqInterExpr<'_, '_, '_> {
res res
} }
#[expect(clippy::similar_names, clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) { if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) {
return false; return false;
@ -463,7 +462,6 @@ impl HirEqInterExpr<'_, '_, '_> {
} }
} }
#[expect(clippy::similar_names)]
fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool { fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
match (left, right) { match (left, right) {
(&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => { (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => {
@ -1159,7 +1157,6 @@ pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 {
h.finish() h.finish()
} }
#[expect(clippy::similar_names)]
fn eq_span_tokens( fn eq_span_tokens(
cx: &LateContext<'_>, cx: &LateContext<'_>,
left: impl SpanRange, left: impl SpanRange,

View file

@ -14,7 +14,7 @@
clippy::missing_panics_doc, clippy::missing_panics_doc,
clippy::must_use_candidate, clippy::must_use_candidate,
rustc::diagnostic_outside_of_impl, rustc::diagnostic_outside_of_impl,
rustc::untranslatable_diagnostic, rustc::untranslatable_diagnostic
)] )]
// warn on the same lints as `clippy_lints` // warn on the same lints as `clippy_lints`
#![warn(trivial_casts, trivial_numeric_casts)] #![warn(trivial_casts, trivial_numeric_casts)]
@ -1308,8 +1308,8 @@ pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
} }
/// Gets the parent node, if any. /// Gets the parent node, if any.
pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> { pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Node<'_> {
Some(tcx.parent_hir_node(id)) tcx.parent_hir_node(id)
} }
/// Gets the parent expression, if any - this is useful to constrain a lint. /// Gets the parent expression, if any - this is useful to constrain a lint.
@ -1321,7 +1321,7 @@ pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'t
/// constraint lints /// constraint lints
pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> { pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
match get_parent_node(cx.tcx, hir_id) { match get_parent_node(cx.tcx, hir_id) {
Some(Node::Expr(parent)) => Some(parent), Node::Expr(parent) => Some(parent),
_ => None, _ => None,
} }
} }
@ -2182,7 +2182,7 @@ pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
/// Checks if the expression is the final expression returned from a block. /// Checks if the expression is the final expression returned from a block.
pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..))) matches!(get_parent_node(tcx, expr.hir_id), Node::Block(..))
} }
pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> { pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {

View file

@ -11,7 +11,7 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
["rustc_lint_defs", "Applicability", "MaybeIncorrect"], ["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
["rustc_lint_defs", "Applicability", "MachineApplicable"], ["rustc_lint_defs", "Applicability", "MachineApplicable"],
]; ];
pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; pub const DIAGNOSTIC_BUILDER: [&str; 2] = ["rustc_errors", "DiagnosticBuilder"];
pub const BINARYHEAP_ITER: [&str; 5] = ["alloc", "collections", "binary_heap", "BinaryHeap", "iter"]; pub const BINARYHEAP_ITER: [&str; 5] = ["alloc", "collections", "binary_heap", "BinaryHeap", "iter"];
pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"]; pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"]; pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];

View file

@ -174,9 +174,8 @@ fn check_rvalue<'tcx>(
)) ))
} }
}, },
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions, _) | Rvalue::ShallowInitBox(_, _) => { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions, _)
Ok(()) | Rvalue::ShallowInitBox(_, _) => Ok(()),
},
Rvalue::UnaryOp(_, operand) => { Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, tcx); let ty = operand.ty(body, tcx);
if ty.is_integral() || ty.is_bool() { if ty.is_integral() || ty.is_bool() {
@ -389,7 +388,6 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
}) })
} }
#[expect(clippy::similar_names)] // bit too pedantic
fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool { fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool {
// FIXME(effects, fee1-dead) revert to const destruct once it works again // FIXME(effects, fee1-dead) revert to const destruct once it works again
#[expect(unused)] #[expect(unused)]

View file

@ -8,7 +8,7 @@ use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
use rustc_lint::{LateContext, LintContext}; use rustc_lint::{LateContext, LintContext};
use rustc_session::Session; use rustc_session::Session;
use rustc_span::source_map::{original_sp, SourceMap}; use rustc_span::source_map::{original_sp, SourceMap};
use rustc_span::{hygiene, BytePos, SourceFileAndLine, Pos, SourceFile, Span, SpanData, SyntaxContext, DUMMY_SP}; use rustc_span::{hygiene, BytePos, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, DUMMY_SP};
use std::borrow::Cow; use std::borrow::Cow;
use std::ops::Range; use std::ops::Range;

View file

@ -272,7 +272,9 @@ pub fn main() {
}, },
_ => Some(s.to_string()), _ => Some(s.to_string()),
}) })
// FIXME: remove this line in 1.79 to only keep `--cfg clippy`.
.chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]) .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()])
.chain(vec!["--cfg".into(), "clippy".into()])
.collect::<Vec<String>>(); .collect::<Vec<String>>();
// We enable Clippy if one of the following conditions is met // We enable Clippy if one of the following conditions is met

View file

@ -4,6 +4,7 @@
#![warn(rust_2018_idioms, unused_lifetimes)] #![warn(rust_2018_idioms, unused_lifetimes)]
#![allow(unused_extern_crates)] #![allow(unused_extern_crates)]
use ui_test::spanned::Spanned;
use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling}; use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -112,20 +113,21 @@ fn base_config(test_dir: &str) -> (Config, Args) {
let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into())); let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into()));
let mut config = Config { let mut config = Config {
mode: Mode::Yolo { output_conflict_handling: OutputConflictHandling::Error,
rustfix: ui_test::RustfixMode::Everything,
},
filter_files: env::var("TESTNAME") filter_files: env::var("TESTNAME")
.map(|filters| filters.split(',').map(str::to_string).collect()) .map(|filters| filters.split(',').map(str::to_string).collect())
.unwrap_or_default(), .unwrap_or_default(),
target: None, target: None,
bless_command: Some("cargo uibless".into()),
out_dir: target_dir.join("ui_test"), out_dir: target_dir.join("ui_test"),
..Config::rustc(Path::new("tests").join(test_dir)) ..Config::rustc(Path::new("tests").join(test_dir))
}; };
config.with_args(&args, /* bless by default */ false); config.comment_defaults.base().mode = Some(Spanned::dummy(Mode::Yolo {
if let OutputConflictHandling::Error(err) = &mut config.output_conflict_handling { rustfix: ui_test::RustfixMode::Everything,
*err = "cargo uibless".into(); }))
} .into();
config.comment_defaults.base().diagnostic_code_prefix = Some(Spanned::dummy("clippy::".into())).into();
config.with_args(&args);
let current_exe_path = env::current_exe().unwrap(); let current_exe_path = env::current_exe().unwrap();
let deps_path = current_exe_path.parent().unwrap(); let deps_path = current_exe_path.parent().unwrap();
let profile_path = deps_path.parent().unwrap(); let profile_path = deps_path.parent().unwrap();
@ -179,9 +181,7 @@ fn run_internal_tests() {
return; return;
} }
let (mut config, args) = base_config("ui-internal"); let (mut config, args) = base_config("ui-internal");
if let OutputConflictHandling::Error(err) = &mut config.output_conflict_handling { config.bless_command = Some("cargo uitest --features internal -- -- --bless".into());
*err = "cargo uitest --features internal -- -- --bless".into();
}
ui_test::run_tests_generic( ui_test::run_tests_generic(
vec![config], vec![config],
@ -196,8 +196,10 @@ fn run_ui_toml() {
let (mut config, args) = base_config("ui-toml"); let (mut config, args) = base_config("ui-toml");
config config
.stderr_filters .comment_defaults
.push((Match::from(env::current_dir().unwrap().as_path()), b"$DIR")); .base()
.normalize_stderr
.push((Match::from(env::current_dir().unwrap().as_path()), b"$DIR".into()));
ui_test::run_tests_generic( ui_test::run_tests_generic(
vec![config], vec![config],
@ -213,6 +215,8 @@ fn run_ui_toml() {
.unwrap(); .unwrap();
} }
// Allow `Default::default` as `OptWithSpan` is not nameable
#[allow(clippy::default_trait_access)]
fn run_ui_cargo() { fn run_ui_cargo() {
if IS_RUSTC_TEST_SUITE { if IS_RUSTC_TEST_SUITE {
return; return;
@ -234,11 +238,13 @@ fn run_ui_cargo() {
} else { } else {
"cargo-clippy" "cargo-clippy"
}); });
config.edition = None; config.comment_defaults.base().edition = Default::default();
config config
.stderr_filters .comment_defaults
.push((Match::from(env::current_dir().unwrap().as_path()), b"$DIR")); .base()
.normalize_stderr
.push((Match::from(env::current_dir().unwrap().as_path()), b"$DIR".into()));
let ignored_32bit = |path: &Path| { let ignored_32bit = |path: &Path| {
// FIXME: for some reason the modules are linted in a different order for this test // FIXME: for some reason the modules are linted in a different order for this test
@ -248,7 +254,8 @@ fn run_ui_cargo() {
ui_test::run_tests_generic( ui_test::run_tests_generic(
vec![config], vec![config],
|path, config| { |path, config| {
path.ends_with("Cargo.toml") && ui_test::default_any_file_filter(path, config) && !ignored_32bit(path) path.ends_with("Cargo.toml")
.then(|| ui_test::default_any_file_filter(path, config) && !ignored_32bit(path))
}, },
|_config, _path, _file_contents| {}, |_config, _path, _file_contents| {},
status_emitter::Text::from(args.format), status_emitter::Text::from(args.format),

View file

@ -1,2 +1,2 @@
warning: using config file `$DIR/$DIR/.clippy.toml`, `$DIR/$DIR/clippy.toml` will be ignored warning: using config file `$DIR/tests/ui-cargo/multiple_config_files/warn/.clippy.toml`, `$DIR/tests/ui-cargo/multiple_config_files/warn/clippy.toml` will be ignored

View file

@ -1,5 +1,5 @@
error: this item has an invalid `clippy::version` attribute error: this item has an invalid `clippy::version` attribute
--> $DIR/check_clippy_version_attribute.rs:40:1 --> tests/ui-internal/check_clippy_version_attribute.rs:40:1
| |
LL | / declare_tool_lint! { LL | / declare_tool_lint! {
LL | | #[clippy::version = "1.2.3.4.5.6"] LL | | #[clippy::version = "1.2.3.4.5.6"]
@ -12,7 +12,7 @@ LL | | }
| |
= help: please use a valid semantic version, see `doc/adding_lints.md` = help: please use a valid semantic version, see `doc/adding_lints.md`
note: the lint level is defined here note: the lint level is defined here
--> $DIR/check_clippy_version_attribute.rs:1:9 --> tests/ui-internal/check_clippy_version_attribute.rs:1:9
| |
LL | #![deny(clippy::internal)] LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
@ -20,7 +20,7 @@ LL | #![deny(clippy::internal)]
= note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
error: this item has an invalid `clippy::version` attribute error: this item has an invalid `clippy::version` attribute
--> $DIR/check_clippy_version_attribute.rs:48:1 --> tests/ui-internal/check_clippy_version_attribute.rs:48:1
| |
LL | / declare_tool_lint! { LL | / declare_tool_lint! {
LL | | #[clippy::version = "I'm a string"] LL | | #[clippy::version = "I'm a string"]
@ -35,7 +35,7 @@ LL | | }
= note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
error: this lint is missing the `clippy::version` attribute or version value error: this lint is missing the `clippy::version` attribute or version value
--> $DIR/check_clippy_version_attribute.rs:59:1 --> tests/ui-internal/check_clippy_version_attribute.rs:59:1
| |
LL | / declare_tool_lint! { LL | / declare_tool_lint! {
LL | | #[clippy::version] LL | | #[clippy::version]
@ -51,7 +51,7 @@ LL | | }
= note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
error: this lint is missing the `clippy::version` attribute or version value error: this lint is missing the `clippy::version` attribute or version value
--> $DIR/check_clippy_version_attribute.rs:67:1 --> tests/ui-internal/check_clippy_version_attribute.rs:67:1
| |
LL | / declare_tool_lint! { LL | / declare_tool_lint! {
LL | | pub clippy::MISSING_ATTRIBUTE_TWO, LL | | pub clippy::MISSING_ATTRIBUTE_TWO,

View file

@ -1,5 +1,5 @@
error: non-standard lint formulation error: non-standard lint formulation
--> $DIR/check_formulation.rs:23:5 --> tests/ui-internal/check_formulation.rs:23:5
| |
LL | /// Check for lint formulations that are correct LL | /// Check for lint formulations that are correct
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -9,7 +9,7 @@ LL | /// Check for lint formulations that are correct
= help: to override `-D warnings` add `#[allow(clippy::almost_standard_lint_formulation)]` = help: to override `-D warnings` add `#[allow(clippy::almost_standard_lint_formulation)]`
error: non-standard lint formulation error: non-standard lint formulation
--> $DIR/check_formulation.rs:33:5 --> tests/ui-internal/check_formulation.rs:33:5
| |
LL | /// Detects uses of incorrect formulations LL | /// Detects uses of incorrect formulations
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -1,5 +1,5 @@
error: this call is collapsible error: this call is collapsible
--> $DIR/collapsible_span_lint_calls.rs:35:9 --> tests/ui-internal/collapsible_span_lint_calls.rs:35:9
| |
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
@ -7,14 +7,14 @@ LL | | });
| |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)` | |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)`
| |
note: the lint level is defined here note: the lint level is defined here
--> $DIR/collapsible_span_lint_calls.rs:1:9 --> tests/ui-internal/collapsible_span_lint_calls.rs:1:9
| |
LL | #![deny(clippy::internal)] LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
= note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
error: this call is collapsible error: this call is collapsible
--> $DIR/collapsible_span_lint_calls.rs:38:9 --> tests/ui-internal/collapsible_span_lint_calls.rs:38:9
| |
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.span_help(expr.span, help_msg); LL | | db.span_help(expr.span, help_msg);
@ -22,7 +22,7 @@ LL | | });
| |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)` | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)`
error: this call is collapsible error: this call is collapsible
--> $DIR/collapsible_span_lint_calls.rs:41:9 --> tests/ui-internal/collapsible_span_lint_calls.rs:41:9
| |
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.help(help_msg); LL | | db.help(help_msg);
@ -30,7 +30,7 @@ LL | | });
| |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
error: this call is collapsible error: this call is collapsible
--> $DIR/collapsible_span_lint_calls.rs:44:9 --> tests/ui-internal/collapsible_span_lint_calls.rs:44:9
| |
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.span_note(expr.span, note_msg); LL | | db.span_note(expr.span, note_msg);
@ -38,7 +38,7 @@ LL | | });
| |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
error: this call is collapsible error: this call is collapsible
--> $DIR/collapsible_span_lint_calls.rs:47:9 --> tests/ui-internal/collapsible_span_lint_calls.rs:47:9
| |
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.note(note_msg); LL | | db.note(note_msg);

View file

@ -1,5 +1,5 @@
error: the lint `COOL_LINT_DEFAULT` has the default deprecation reason error: the lint `COOL_LINT_DEFAULT` has the default deprecation reason
--> $DIR/default_deprecation_reason.rs:8:1 --> tests/ui-internal/default_deprecation_reason.rs:8:1
| |
LL | / declare_deprecated_lint! { LL | / declare_deprecated_lint! {
LL | | /// ### What it does LL | | /// ### What it does
@ -11,7 +11,7 @@ LL | | }
| |_^ | |_^
| |
note: the lint level is defined here note: the lint level is defined here
--> $DIR/default_deprecation_reason.rs:1:9 --> tests/ui-internal/default_deprecation_reason.rs:1:9
| |
LL | #![deny(clippy::internal)] LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^

View file

@ -1,5 +1,5 @@
error: the lint `TEST_LINT_DEFAULT` has the default lint description error: the lint `TEST_LINT_DEFAULT` has the default lint description
--> $DIR/default_lint.rs:18:1 --> tests/ui-internal/default_lint.rs:18:1
| |
LL | / declare_tool_lint! { LL | / declare_tool_lint! {
LL | | pub clippy::TEST_LINT_DEFAULT, LL | | pub clippy::TEST_LINT_DEFAULT,
@ -10,7 +10,7 @@ LL | | }
| |_^ | |_^
| |
note: the lint level is defined here note: the lint level is defined here
--> $DIR/default_lint.rs:1:9 --> tests/ui-internal/default_lint.rs:1:9
| |
LL | #![deny(clippy::internal)] LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^

View file

@ -1,5 +1,5 @@
error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` error: use of a disallowed method `rustc_lint::context::LintContext::span_lint`
--> $DIR/disallow_span_lint.rs:14:5 --> tests/ui-internal/disallow_span_lint.rs:14:5
| |
LL | cx.span_lint(lint, span, msg, |_| {}); LL | cx.span_lint(lint, span, msg, |_| {});
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -8,7 +8,7 @@ LL | cx.span_lint(lint, span, msg, |_| {});
= help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]`
error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint` error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint`
--> $DIR/disallow_span_lint.rs:24:5 --> tests/ui-internal/disallow_span_lint.rs:24:5
| |
LL | tcx.node_span_lint(lint, hir_id, span, msg, |_| {}); LL | tcx.node_span_lint(lint, hir_id, span, msg, |_| {});
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -1,30 +1,30 @@
error: interning a defined symbol error: interning a defined symbol
--> $DIR/interning_defined_symbol.rs:17:13 --> tests/ui-internal/interning_defined_symbol.rs:17:13
| |
LL | let _ = Symbol::intern("f32"); LL | let _ = Symbol::intern("f32");
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32` | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32`
| |
note: the lint level is defined here note: the lint level is defined here
--> $DIR/interning_defined_symbol.rs:1:9 --> tests/ui-internal/interning_defined_symbol.rs:1:9
| |
LL | #![deny(clippy::internal)] LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
= note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]` = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]`
error: interning a defined symbol error: interning a defined symbol
--> $DIR/interning_defined_symbol.rs:20:13 --> tests/ui-internal/interning_defined_symbol.rs:20:13
| |
LL | let _ = sym!(f32); LL | let _ = sym!(f32);
| ^^^^^^^^^ help: try: `rustc_span::sym::f32` | ^^^^^^^^^ help: try: `rustc_span::sym::f32`
error: interning a defined symbol error: interning a defined symbol
--> $DIR/interning_defined_symbol.rs:23:13 --> tests/ui-internal/interning_defined_symbol.rs:23:13
| |
LL | let _ = Symbol::intern("proc-macro"); LL | let _ = Symbol::intern("proc-macro");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro`
error: interning a defined symbol error: interning a defined symbol
--> $DIR/interning_defined_symbol.rs:26:13 --> tests/ui-internal/interning_defined_symbol.rs:26:13
| |
LL | let _ = Symbol::intern("self"); LL | let _ = Symbol::intern("self");
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::kw::SelfLower` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::kw::SelfLower`

View file

@ -1,11 +1,11 @@
error: `extract_msrv_attr!` macro missing from `LateLintPass` implementation error: `extract_msrv_attr!` macro missing from `LateLintPass` implementation
--> $DIR/invalid_msrv_attr_impl.rs:28:1 --> tests/ui-internal/invalid_msrv_attr_impl.rs:28:1
| |
LL | impl LateLintPass<'_> for Pass { LL | impl LateLintPass<'_> for Pass {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
note: the lint level is defined here note: the lint level is defined here
--> $DIR/invalid_msrv_attr_impl.rs:1:9 --> tests/ui-internal/invalid_msrv_attr_impl.rs:1:9
| |
LL | #![deny(clippy::internal)] LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
@ -17,7 +17,7 @@ LL + extract_msrv_attr!(LateContext);
| |
error: `extract_msrv_attr!` macro missing from `EarlyLintPass` implementation error: `extract_msrv_attr!` macro missing from `EarlyLintPass` implementation
--> $DIR/invalid_msrv_attr_impl.rs:32:1 --> tests/ui-internal/invalid_msrv_attr_impl.rs:32:1
| |
LL | impl EarlyLintPass for Pass { LL | impl EarlyLintPass for Pass {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -1,5 +1,5 @@
error: invalid path error: invalid path
--> $DIR/invalid_paths.rs:15:5 --> tests/ui-internal/invalid_paths.rs:15:5
| |
LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -8,13 +8,13 @@ LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"
= help: to override `-D warnings` add `#[allow(clippy::invalid_paths)]` = help: to override `-D warnings` add `#[allow(clippy::invalid_paths)]`
error: invalid path error: invalid path
--> $DIR/invalid_paths.rs:18:5 --> tests/ui-internal/invalid_paths.rs:18:5
| |
LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: invalid path error: invalid path
--> $DIR/invalid_paths.rs:21:5 --> tests/ui-internal/invalid_paths.rs:21:5
| |
LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -1,5 +1,5 @@
error: the lint `TEST_LINT` is not added to any `LintPass` error: the lint `TEST_LINT` is not added to any `LintPass`
--> $DIR/lint_without_lint_pass.rs:12:1 --> tests/ui-internal/lint_without_lint_pass.rs:12:1
| |
LL | / declare_tool_lint! { LL | / declare_tool_lint! {
LL | | pub clippy::TEST_LINT, LL | | pub clippy::TEST_LINT,
@ -10,7 +10,7 @@ LL | | }
| |_^ | |_^
| |
note: the lint level is defined here note: the lint level is defined here
--> $DIR/lint_without_lint_pass.rs:1:9 --> tests/ui-internal/lint_without_lint_pass.rs:1:9
| |
LL | #![deny(clippy::internal)] LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^

View file

@ -1,11 +1,11 @@
error: usage of `outer_expn().expn_data()` error: usage of `outer_expn().expn_data()`
--> $DIR/outer_expn_data.rs:23:34 --> tests/ui-internal/outer_expn_data.rs:23:34
| |
LL | let _ = expr.span.ctxt().outer_expn().expn_data(); LL | let _ = expr.span.ctxt().outer_expn().expn_data();
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()` | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()`
| |
note: the lint level is defined here note: the lint level is defined here
--> $DIR/outer_expn_data.rs:1:9 --> tests/ui-internal/outer_expn_data.rs:1:9
| |
LL | #![deny(clippy::internal)] LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^

View file

@ -1,72 +1,72 @@
error: use of a def path to a diagnostic item error: use of a def path to a diagnostic item
--> $DIR/unnecessary_def_path.rs:36:13 --> tests/ui-internal/unnecessary_def_path.rs:36:13
| |
LL | let _ = match_type(cx, ty, &OPTION); LL | let _ = match_type(cx, ty, &OPTION);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
| |
note: the lint level is defined here note: the lint level is defined here
--> $DIR/unnecessary_def_path.rs:2:9 --> tests/ui-internal/unnecessary_def_path.rs:2:9
| |
LL | #![deny(clippy::internal)] LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
= note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]` = note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]`
error: use of a def path to a diagnostic item error: use of a def path to a diagnostic item
--> $DIR/unnecessary_def_path.rs:37:13 --> tests/ui-internal/unnecessary_def_path.rs:37:13
| |
LL | let _ = match_type(cx, ty, RESULT); LL | let _ = match_type(cx, ty, RESULT);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
error: use of a def path to a diagnostic item error: use of a def path to a diagnostic item
--> $DIR/unnecessary_def_path.rs:38:13 --> tests/ui-internal/unnecessary_def_path.rs:38:13
| |
LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); LL | let _ = match_type(cx, ty, &["core", "result", "Result"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
error: use of a def path to a diagnostic item error: use of a def path to a diagnostic item
--> $DIR/unnecessary_def_path.rs:42:13 --> tests/ui-internal/unnecessary_def_path.rs:42:13
| |
LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)`
error: use of a def path to a diagnostic item error: use of a def path to a diagnostic item
--> $DIR/unnecessary_def_path.rs:44:13 --> tests/ui-internal/unnecessary_def_path.rs:44:13
| |
LL | let _ = match_type(cx, ty, &paths::OPTION); LL | let _ = match_type(cx, ty, &paths::OPTION);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
error: use of a def path to a diagnostic item error: use of a def path to a diagnostic item
--> $DIR/unnecessary_def_path.rs:45:13 --> tests/ui-internal/unnecessary_def_path.rs:45:13
| |
LL | let _ = match_type(cx, ty, paths::RESULT); LL | let _ = match_type(cx, ty, paths::RESULT);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
error: use of a def path to a `LangItem` error: use of a def path to a `LangItem`
--> $DIR/unnecessary_def_path.rs:47:13 --> tests/ui-internal/unnecessary_def_path.rs:47:13
| |
LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)`
error: use of a def path to a diagnostic item error: use of a def path to a diagnostic item
--> $DIR/unnecessary_def_path.rs:48:13 --> tests/ui-internal/unnecessary_def_path.rs:48:13
| |
LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)`
error: use of a def path to a `LangItem` error: use of a def path to a `LangItem`
--> $DIR/unnecessary_def_path.rs:50:13 --> tests/ui-internal/unnecessary_def_path.rs:50:13
| |
LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)`
error: use of a def path to a diagnostic item error: use of a def path to a diagnostic item
--> $DIR/unnecessary_def_path.rs:51:13 --> tests/ui-internal/unnecessary_def_path.rs:51:13
| |
LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]); LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)`
error: use of a def path to a `LangItem` error: use of a def path to a `LangItem`
--> $DIR/unnecessary_def_path.rs:52:13 --> tests/ui-internal/unnecessary_def_path.rs:52:13
| |
LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)`
@ -74,25 +74,25 @@ LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]);
= help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead
error: use of a def path to a diagnostic item error: use of a def path to a diagnostic item
--> $DIR/unnecessary_def_path.rs:54:13 --> tests/ui-internal/unnecessary_def_path.rs:54:13
| |
LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)`
error: use of a def path to a diagnostic item error: use of a def path to a diagnostic item
--> $DIR/unnecessary_def_path.rs:56:13 --> tests/ui-internal/unnecessary_def_path.rs:56:13
| |
LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)`
error: use of a def path to a `LangItem` error: use of a def path to a `LangItem`
--> $DIR/unnecessary_def_path.rs:57:13 --> tests/ui-internal/unnecessary_def_path.rs:57:13
| |
LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))`
error: use of a def path to a `LangItem` error: use of a def path to a `LangItem`
--> $DIR/unnecessary_def_path.rs:58:13 --> tests/ui-internal/unnecessary_def_path.rs:58:13
| |
LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)`

View file

@ -1,5 +1,5 @@
error: hardcoded path to a diagnostic item error: hardcoded path to a diagnostic item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:10:36
| |
LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -9,7 +9,7 @@ LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]`
error: hardcoded path to a language item error: hardcoded path to a language item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:11:40
| |
LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -17,10 +17,10 @@ LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]
= help: convert all references to use `LangItem::DerefMut` = help: convert all references to use `LangItem::DerefMut`
error: hardcoded path to a diagnostic item error: hardcoded path to a diagnostic item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:43
| |
LL | const OPS_MOD: [&str; 5] = ["core", "ops"]; LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: convert all references to use `sym::deref_method` = help: convert all references to use `sym::deref_method`

View file

@ -1,36 +1,36 @@
error: unnecessary `Symbol` to string conversion error: unnecessary `Symbol` to string conversion
--> $DIR/unnecessary_symbol_str.rs:15:5 --> tests/ui-internal/unnecessary_symbol_str.rs:15:5
| |
LL | Symbol::intern("foo").as_str() == "clippy"; LL | Symbol::intern("foo").as_str() == "clippy";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy`
| |
note: the lint level is defined here note: the lint level is defined here
--> $DIR/unnecessary_symbol_str.rs:2:9 --> tests/ui-internal/unnecessary_symbol_str.rs:2:9
| |
LL | #![deny(clippy::internal)] LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
= note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]`
error: unnecessary `Symbol` to string conversion error: unnecessary `Symbol` to string conversion
--> $DIR/unnecessary_symbol_str.rs:16:5 --> tests/ui-internal/unnecessary_symbol_str.rs:16:5
| |
LL | Symbol::intern("foo").to_string() == "self"; LL | Symbol::intern("foo").to_string() == "self";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower`
error: unnecessary `Symbol` to string conversion error: unnecessary `Symbol` to string conversion
--> $DIR/unnecessary_symbol_str.rs:17:5 --> tests/ui-internal/unnecessary_symbol_str.rs:17:5
| |
LL | Symbol::intern("foo").to_ident_string() != "Self"; LL | Symbol::intern("foo").to_ident_string() != "Self";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper`
error: unnecessary `Symbol` to string conversion error: unnecessary `Symbol` to string conversion
--> $DIR/unnecessary_symbol_str.rs:18:5 --> tests/ui-internal/unnecessary_symbol_str.rs:18:5
| |
LL | &*Ident::empty().as_str() == "clippy"; LL | &*Ident::empty().as_str() == "clippy";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy`
error: unnecessary `Symbol` to string conversion error: unnecessary `Symbol` to string conversion
--> $DIR/unnecessary_symbol_str.rs:19:5 --> tests/ui-internal/unnecessary_symbol_str.rs:19:5
| |
LL | "clippy" == Ident::empty().to_string(); LL | "clippy" == Ident::empty().to_string();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name`

View file

@ -1,5 +1,5 @@
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:40:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:5
| |
LL | std::f32::MAX; LL | std::f32::MAX;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
@ -8,19 +8,19 @@ LL | std::f32::MAX;
= help: to override `-D warnings` add `#[allow(clippy::absolute_paths)]` = help: to override `-D warnings` add `#[allow(clippy::absolute_paths)]`
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:41:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:41:5
| |
LL | core::f32::MAX; LL | core::f32::MAX;
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:42:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:42:5
| |
LL | ::core::f32::MAX; LL | ::core::f32::MAX;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:58:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:58:5
| |
LL | ::std::f32::MAX; LL | ::std::f32::MAX;
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^

View file

@ -1,5 +1,5 @@
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:40:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:5
| |
LL | std::f32::MAX; LL | std::f32::MAX;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
@ -8,61 +8,61 @@ LL | std::f32::MAX;
= help: to override `-D warnings` add `#[allow(clippy::absolute_paths)]` = help: to override `-D warnings` add `#[allow(clippy::absolute_paths)]`
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:41:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:41:5
| |
LL | core::f32::MAX; LL | core::f32::MAX;
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:42:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:42:5
| |
LL | ::core::f32::MAX; LL | ::core::f32::MAX;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:43:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:43:5
| |
LL | crate::a::b::c::C; LL | crate::a::b::c::C;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:44:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:44:5
| |
LL | crate::a::b::c::d::e::f::F; LL | crate::a::b::c::d::e::f::F;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:45:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:45:5
| |
LL | crate::a::A; LL | crate::a::A;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:46:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:46:5
| |
LL | crate::a::b::B; LL | crate::a::b::B;
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:47:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:47:5
| |
LL | crate::a::b::c::C::ZERO; LL | crate::a::b::c::C::ZERO;
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:48:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:48:5
| |
LL | helper::b::c::d::e::f(); LL | helper::b::c::d::e::f();
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:49:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:49:5
| |
LL | ::helper::b::c::d::e::f(); LL | ::helper::b::c::d::e::f();
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^
error: consider bringing this path into scope with the `use` keyword error: consider bringing this path into scope with the `use` keyword
--> $DIR/absolute_paths.rs:58:5 --> tests/ui-toml/absolute_paths/absolute_paths.rs:58:5
| |
LL | ::std::f32::MAX; LL | ::std::f32::MAX;
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^

View file

@ -1,5 +1,5 @@
error: variables can be used directly in the `format!` string error: variables can be used directly in the `format!` string
--> $DIR/uninlined_format_args.rs:9:5 --> tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs:9:5
| |
LL | println!("val='{}'", local_i32); LL | println!("val='{}'", local_i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -13,7 +13,7 @@ LL + println!("val='{local_i32}'");
| |
error: variables can be used directly in the `format!` string error: variables can be used directly in the `format!` string
--> $DIR/uninlined_format_args.rs:10:5 --> tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs:10:5
| |
LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -25,7 +25,7 @@ LL + println!("Hello {} is {local_f64:.local_i32$}", "x");
| |
error: literal with an empty format string error: literal with an empty format string
--> $DIR/uninlined_format_args.rs:10:35 --> tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs:10:35
| |
LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64);
| ^^^ | ^^^
@ -39,7 +39,7 @@ LL + println!("Hello x is {:.*}", local_i32, local_f64);
| |
error: variables can be used directly in the `format!` string error: variables can be used directly in the `format!` string
--> $DIR/uninlined_format_args.rs:11:5 --> tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs:11:5
| |
LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64); LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -51,7 +51,7 @@ LL + println!("Hello {local_i32} is {local_f64:.*}", 5);
| |
error: variables can be used directly in the `format!` string error: variables can be used directly in the `format!` string
--> $DIR/uninlined_format_args.rs:12:5 --> tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs:12:5
| |
LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64); LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -63,7 +63,7 @@ LL + println!("Hello {local_i32} is {local_f64:.*}", 5);
| |
error: variables can be used directly in the `format!` string error: variables can be used directly in the `format!` string
--> $DIR/uninlined_format_args.rs:13:5 --> tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs:13:5
| |
LL | println!("{}, {}", local_i32, local_opt.unwrap()); LL | println!("{}, {}", local_i32, local_opt.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -1,5 +1,5 @@
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects_allowed.rs:69:13 --> tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs:69:13
| |
LL | let _ = Baz + Baz; LL | let _ = Baz + Baz;
| ^^^^^^^^^ | ^^^^^^^^^
@ -8,49 +8,49 @@ LL | let _ = Baz + Baz;
= help: to override `-D warnings` add `#[allow(clippy::arithmetic_side_effects)]` = help: to override `-D warnings` add `#[allow(clippy::arithmetic_side_effects)]`
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects_allowed.rs:80:13 --> tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs:80:13
| |
LL | let _ = 1i32 + Baz; LL | let _ = 1i32 + Baz;
| ^^^^^^^^^^ | ^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects_allowed.rs:83:13 --> tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs:83:13
| |
LL | let _ = 1i64 + Foo; LL | let _ = 1i64 + Foo;
| ^^^^^^^^^^ | ^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects_allowed.rs:87:13 --> tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs:87:13
| |
LL | let _ = 1i64 + Baz; LL | let _ = 1i64 + Baz;
| ^^^^^^^^^^ | ^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects_allowed.rs:98:13 --> tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs:98:13
| |
LL | let _ = Baz + 1i32; LL | let _ = Baz + 1i32;
| ^^^^^^^^^^ | ^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects_allowed.rs:101:13 --> tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs:101:13
| |
LL | let _ = Foo + 1i64; LL | let _ = Foo + 1i64;
| ^^^^^^^^^^ | ^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects_allowed.rs:105:13 --> tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs:105:13
| |
LL | let _ = Baz + 1i64; LL | let _ = Baz + 1i64;
| ^^^^^^^^^^ | ^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects_allowed.rs:114:13 --> tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs:114:13
| |
LL | let _ = -Bar; LL | let _ = -Bar;
| ^^^^ | ^^^^
error: arithmetic operation that can potentially result in unexpected side-effects error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects_allowed.rs:116:13 --> tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs:116:13
| |
LL | let _ = -Baz; LL | let _ = -Baz;
| ^^^^ | ^^^^

View file

@ -1,5 +1,5 @@
error: large array defined as const error: large array defined as const
--> $DIR/array_size_threshold.rs:4:1 --> tests/ui-toml/array_size_threshold/array_size_threshold.rs:4:1
| |
LL | const ABOVE: [u8; 11] = [0; 11]; LL | const ABOVE: [u8; 11] = [0; 11];
| -----^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -10,7 +10,7 @@ LL | const ABOVE: [u8; 11] = [0; 11];
= help: to override `-D warnings` add `#[allow(clippy::large_const_arrays)]` = help: to override `-D warnings` add `#[allow(clippy::large_const_arrays)]`
error: allocating a local array larger than 10 bytes error: allocating a local array larger than 10 bytes
--> $DIR/array_size_threshold.rs:4:25 --> tests/ui-toml/array_size_threshold/array_size_threshold.rs:4:25
| |
LL | const ABOVE: [u8; 11] = [0; 11]; LL | const ABOVE: [u8; 11] = [0; 11];
| ^^^^^^^ | ^^^^^^^
@ -20,7 +20,7 @@ LL | const ABOVE: [u8; 11] = [0; 11];
= help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]` = help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]`
error: allocating a local array larger than 10 bytes error: allocating a local array larger than 10 bytes
--> $DIR/array_size_threshold.rs:8:17 --> tests/ui-toml/array_size_threshold/array_size_threshold.rs:8:17
| |
LL | let above = [0u8; 11]; LL | let above = [0u8; 11];
| ^^^^^^^^^ | ^^^^^^^^^

View file

@ -1,5 +1,5 @@
error: `std::string::String` may not be held across an `await` point per `clippy.toml` error: `std::string::String` may not be held across an `await` point per `clippy.toml`
--> $DIR/await_holding_invalid_type.rs:5:9 --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:5:9
| |
LL | let _x = String::from("hello"); LL | let _x = String::from("hello");
| ^^ | ^^
@ -9,13 +9,13 @@ LL | let _x = String::from("hello");
= help: to override `-D warnings` add `#[allow(clippy::await_holding_invalid_type)]` = help: to override `-D warnings` add `#[allow(clippy::await_holding_invalid_type)]`
error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml` error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml`
--> $DIR/await_holding_invalid_type.rs:10:9 --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:10:9
| |
LL | let x = Ipv4Addr::new(127, 0, 0, 1); LL | let x = Ipv4Addr::new(127, 0, 0, 1);
| ^ | ^
error: `std::string::String` may not be held across an `await` point per `clippy.toml` error: `std::string::String` may not be held across an `await` point per `clippy.toml`
--> $DIR/await_holding_invalid_type.rs:33:13 --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:33:13
| |
LL | let _x = String::from("hi!"); LL | let _x = String::from("hi!");
| ^^ | ^^

View file

@ -1,5 +1,5 @@
error: error reading Clippy's configuration file: expected `.`, `=` error: error reading Clippy's configuration file: expected `.`, `=`
--> $DIR/$DIR/clippy.toml:1:4 --> $DIR/tests/ui-toml/bad_toml/clippy.toml:1:4
| |
LL | fn this_is_obviously(not: a, toml: file) { LL | fn this_is_obviously(not: a, toml: file) {
| ^ | ^

View file

@ -1,5 +1,5 @@
error: error reading Clippy's configuration file: invalid type: integer `42`, expected a sequence error: error reading Clippy's configuration file: invalid type: integer `42`, expected a sequence
--> $DIR/$DIR/clippy.toml:1:20 --> $DIR/tests/ui-toml/bad_toml_type/clippy.toml:1:20
| |
LL | disallowed-names = 42 LL | disallowed-names = 42
| ^^ | ^^

View file

@ -1,17 +1,17 @@
warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
--> $DIR/$DIR/clippy.toml:2:1 --> $DIR/tests/ui-toml/conf_deprecated_key/clippy.toml:2:1
| |
LL | cyclomatic-complexity-threshold = 2 LL | cyclomatic-complexity-threshold = 2
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: error reading Clippy's configuration file: deprecated field `blacklisted-names`. Please use `disallowed-names` instead warning: error reading Clippy's configuration file: deprecated field `blacklisted-names`. Please use `disallowed-names` instead
--> $DIR/$DIR/clippy.toml:3:1 --> $DIR/tests/ui-toml/conf_deprecated_key/clippy.toml:3:1
| |
LL | blacklisted-names = [ "..", "wibble" ] LL | blacklisted-names = [ "..", "wibble" ]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
error: the function has a cognitive complexity of (3/2) error: the function has a cognitive complexity of (3/2)
--> $DIR/conf_deprecated_key.rs:6:4 --> tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs:6:4
| |
LL | fn cognitive_complexity() { LL | fn cognitive_complexity() {
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^

View file

@ -1,5 +1,5 @@
error: the `dbg!` macro is intended as a debugging tool error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:5:22 --> tests/ui-toml/dbg_macro/dbg_macro.rs:5:22
| |
LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
@ -12,7 +12,7 @@ LL | if let Some(n) = n.checked_sub(4) { n } else { n }
| ~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~
error: the `dbg!` macro is intended as a debugging tool error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:9:8 --> tests/ui-toml/dbg_macro/dbg_macro.rs:9:8
| |
LL | if dbg!(n <= 1) { LL | if dbg!(n <= 1) {
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
@ -23,7 +23,7 @@ LL | if n <= 1 {
| ~~~~~~ | ~~~~~~
error: the `dbg!` macro is intended as a debugging tool error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:10:9 --> tests/ui-toml/dbg_macro/dbg_macro.rs:10:9
| |
LL | dbg!(1) LL | dbg!(1)
| ^^^^^^^ | ^^^^^^^
@ -34,7 +34,7 @@ LL | 1
| |
error: the `dbg!` macro is intended as a debugging tool error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:12:9 --> tests/ui-toml/dbg_macro/dbg_macro.rs:12:9
| |
LL | dbg!(n * factorial(n - 1)) LL | dbg!(n * factorial(n - 1))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -45,7 +45,7 @@ LL | n * factorial(n - 1)
| |
error: the `dbg!` macro is intended as a debugging tool error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:17:5 --> tests/ui-toml/dbg_macro/dbg_macro.rs:17:5
| |
LL | dbg!(42); LL | dbg!(42);
| ^^^^^^^^ | ^^^^^^^^
@ -56,7 +56,7 @@ LL | 42;
| ~~ | ~~
error: the `dbg!` macro is intended as a debugging tool error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:18:5 --> tests/ui-toml/dbg_macro/dbg_macro.rs:18:5
| |
LL | dbg!(dbg!(dbg!(42))); LL | dbg!(dbg!(dbg!(42)));
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
@ -67,7 +67,7 @@ LL | dbg!(dbg!(42));
| ~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~
error: the `dbg!` macro is intended as a debugging tool error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:19:14 --> tests/ui-toml/dbg_macro/dbg_macro.rs:19:14
| |
LL | foo(3) + dbg!(factorial(4)); LL | foo(3) + dbg!(factorial(4));
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
@ -78,7 +78,7 @@ LL | foo(3) + factorial(4);
| ~~~~~~~~~~~~ | ~~~~~~~~~~~~
error: the `dbg!` macro is intended as a debugging tool error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:20:5 --> tests/ui-toml/dbg_macro/dbg_macro.rs:20:5
| |
LL | dbg!(1, 2, dbg!(3, 4)); LL | dbg!(1, 2, dbg!(3, 4));
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
@ -89,7 +89,7 @@ LL | (1, 2, dbg!(3, 4));
| ~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~
error: the `dbg!` macro is intended as a debugging tool error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:21:5 --> tests/ui-toml/dbg_macro/dbg_macro.rs:21:5
| |
LL | dbg!(1, 2, 3, 4, 5); LL | dbg!(1, 2, 3, 4, 5);
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^

View file

@ -1,5 +1,5 @@
error: integer literal has a better hexadecimal representation error: integer literal has a better hexadecimal representation
--> $DIR/decimal_literal_representation.rs:4:13 --> tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs:4:13
| |
LL | let _ = 16777215; LL | let _ = 16777215;
| ^^^^^^^^ help: consider: `0x00FF_FFFF` | ^^^^^^^^ help: consider: `0x00FF_FFFF`

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