mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-27 01:04:03 +00:00
Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
dc0bb69e66
4
.github/driver.sh
vendored
4
.github/driver.sh
vendored
@ -32,7 +32,7 @@ test "$sysroot" = $desired_sysroot
|
||||
)
|
||||
|
||||
# Check that the --sysroot argument is only passed once via arg_file.txt (SYSROOT is ignored)
|
||||
(
|
||||
(
|
||||
echo "fn main() {}" > target/driver_test.rs
|
||||
echo "--sysroot="$(./target/debug/clippy-driver --print sysroot)"" > 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
|
||||
# FIXME: How to match the clippy invocation in compile-test.rs?
|
||||
./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2>double_neg.stderr && exit 1
|
||||
sed -e "s,tests/ui,\$DIR," -e "/= help: 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
|
||||
|
||||
# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same
|
||||
|
@ -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_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_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
|
||||
[`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
|
||||
@ -5725,6 +5726,7 @@ Released 2018-09-13
|
||||
[`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_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_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
|
||||
|
@ -27,10 +27,10 @@ rustc_tools_util = "0.3.0"
|
||||
tempfile = { version = "3.2", optional = true }
|
||||
termize = "0.1"
|
||||
color-print = "0.3.4"
|
||||
anstream = "0.5.0"
|
||||
anstream = "0.6.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ui_test = "0.21.2"
|
||||
ui_test = "0.22.1"
|
||||
tester = "0.9"
|
||||
regex = "1.5"
|
||||
toml = "0.7.3"
|
||||
|
@ -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
|
||||
[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
|
||||
#[cfg(not(feature = "cargo-clippy"))]
|
||||
#[cfg(not(clippy)]
|
||||
include!(concat!(env!("OUT_DIR"), "/my_big_function-generated.rs"));
|
||||
|
||||
#[cfg(feature = "cargo-clippy")]
|
||||
#[cfg(clippy)]
|
||||
fn my_big_function(_input: &str) -> Option<MyStruct> {
|
||||
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.
|
||||
|
@ -82,7 +82,7 @@ The output looks something like this (from the example earlier):
|
||||
|
||||
```text
|
||||
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 {}
|
||||
| ^^^^^^^^ help: use: `1..=1`
|
||||
@ -135,14 +135,14 @@ Examples:
|
||||
|
||||
```text
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::forget-ref` implied by `-D warnings`
|
||||
note: argument has type &SomeStruct
|
||||
--> $DIR/drop_forget_ref.rs:10:12
|
||||
--> tests/ui/drop_forget_ref.rs:10:12
|
||||
|
|
||||
10 | forget(&SomeStruct);
|
||||
| ^^^^^^^^^^^
|
||||
@ -158,7 +158,7 @@ Example:
|
||||
|
||||
```text
|
||||
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;
|
||||
| ^^^^^^^^^^^^
|
||||
@ -176,7 +176,7 @@ Example:
|
||||
|
||||
```text
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
|
||||
|
@ -97,19 +97,19 @@ failures:
|
||||
---- compile_test stdout ----
|
||||
normalized stderr:
|
||||
error: function called "foo"
|
||||
--> $DIR/foo_functions.rs:6:12
|
||||
--> tests/ui/foo_functions.rs:6:12
|
||||
|
|
||||
LL | pub fn foo(&self) {}
|
||||
| ^^^
|
||||
|
|
||||
= note: `-D clippy::foo-functions` implied by `-D warnings`
|
||||
error: function called "foo"
|
||||
--> $DIR/foo_functions.rs:13:8
|
||||
--> tests/ui/foo_functions.rs:13:8
|
||||
|
|
||||
LL | fn foo(&self) {}
|
||||
| ^^^
|
||||
error: function called "foo"
|
||||
--> $DIR/foo_functions.rs:19:4
|
||||
--> tests/ui/foo_functions.rs:19:4
|
||||
|
|
||||
LL | fn foo() {}
|
||||
| ^^^
|
||||
|
@ -278,7 +278,7 @@ The minimum number of struct fields for the lints about field names to trigger
|
||||
|
||||
---
|
||||
**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`
|
||||
|
@ -325,7 +325,7 @@ define_Conf! {
|
||||
///
|
||||
/// The minimum number of enum variants for the lints about variant names to trigger
|
||||
(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
|
||||
(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.disallowed_names, DEFAULT_DISALLOWED_NAMES);
|
||||
// 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
|
||||
.allowed_idents_below_min_chars
|
||||
.extend(DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string));
|
||||
|
@ -6,7 +6,7 @@
|
||||
clippy::missing_panics_doc,
|
||||
rustc::diagnostic_outside_of_impl,
|
||||
rustc::untranslatable_diagnostic,
|
||||
rustc::untranslatable_diagnostic_trivial,
|
||||
rustc::untranslatable_diagnostic_trivial
|
||||
)]
|
||||
|
||||
extern crate rustc_ast;
|
||||
|
@ -2,8 +2,11 @@ use std::fmt;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
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_span::Span;
|
||||
use rustc_target::asm::InlineAsmArch;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
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) {
|
||||
if let ExprKind::InlineAsm(ref inline_asm) = expr.kind {
|
||||
fn check_asm_syntax(
|
||||
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) {
|
||||
AsmStyle::Att
|
||||
} else {
|
||||
@ -43,7 +52,7 @@ fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
span,
|
||||
&format!("{style} x86 assembly syntax used"),
|
||||
None,
|
||||
&format!("use {} x86 assembly syntax", !style),
|
||||
@ -89,7 +98,15 @@ declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]);
|
||||
|
||||
impl EarlyLintPass for InlineAsmX86IntelSyntax {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
//! checks for attributes
|
||||
|
||||
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::macros::{is_panic, macro_backtrace};
|
||||
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"
|
||||
}
|
||||
|
||||
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 => [
|
||||
ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
INLINE_ALWAYS,
|
||||
@ -794,6 +846,8 @@ impl_lint_pass!(EarlyAttributes => [
|
||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
NON_MINIMAL_CFG,
|
||||
MAYBE_MISUSED_CFG,
|
||||
DEPRECATED_CLIPPY_CFG_ATTR,
|
||||
UNNECESSARY_CLIPPY_CFG,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for EarlyAttributes {
|
||||
@ -803,6 +857,7 @@ impl EarlyLintPass for EarlyAttributes {
|
||||
|
||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
check_deprecated_cfg_attr(cx, attr, &self.msrv);
|
||||
check_deprecated_cfg(cx, attr);
|
||||
check_mismatched_target_os(cx, attr);
|
||||
check_minimal_cfg_condition(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) {
|
||||
if msrv.meets(msrvs::TOOL_ATTRIBUTES)
|
||||
// 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
|
||||
{
|
||||
fn check_cargo_clippy_attr(cx: &EarlyContext<'_>, item: &rustc_ast::MetaItem) {
|
||||
if item.has_name(sym::feature) && item.value_str().is_some_and(|v| v.as_str() == "cargo-clippy") {
|
||||
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(),
|
||||
DEPRECATED_CLIPPY_CFG_ATTR,
|
||||
item.span,
|
||||
"`feature = \"cargo-clippy\"` was replaced by `clippy`",
|
||||
"replace with",
|
||||
"clippy".to_string(),
|
||||
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]) {
|
||||
for item in items {
|
||||
if let NestedMetaItem::MetaItem(meta) = item {
|
||||
|
@ -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::ty::implements_trait;
|
||||
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 rustc_errors::Applicability;
|
||||
use rustc_hir::{BlockCheckMode, Expr, ExprKind, MatchSource};
|
||||
@ -13,7 +13,7 @@ use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### 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.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
@ -25,6 +25,11 @@ declare_clippy_lint! {
|
||||
/// if { true } { /* ... */ }
|
||||
///
|
||||
/// if { let x = somefunc(); x } { /* ... */ }
|
||||
///
|
||||
/// match { let e = somefunc(); e } {
|
||||
/// // ...
|
||||
/// # _ => {}
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
@ -34,6 +39,12 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// let res = { let x = somefunc(); x };
|
||||
/// if res { /* ... */ }
|
||||
///
|
||||
/// let res = { let e = somefunc(); e };
|
||||
/// match res {
|
||||
/// // ...
|
||||
/// # _ => {}
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.45.0"]
|
||||
pub BLOCKS_IN_CONDITIONS,
|
||||
@ -94,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions {
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
// move block higher
|
||||
|
@ -85,7 +85,117 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
|
||||
) {
|
||||
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> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
}
|
||||
|
@ -101,25 +101,22 @@ impl<'tcx> Visitor<'tcx> for InferVisitor {
|
||||
|
||||
fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
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();
|
||||
v.visit_ty(ty);
|
||||
!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),
|
||||
..
|
||||
})
|
||||
| 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)
|
||||
&& let Some(sig) = expr_sig(cx, path)
|
||||
&& let Some(input) = sig.input(index)
|
||||
|
@ -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 suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
|
||||
(false, false) if from_nbits > to_nbits => "",
|
||||
(true, false) if from_nbits > to_nbits => "",
|
||||
(_, false) if from_nbits > to_nbits => "",
|
||||
(false, true) if from_nbits > 64 => "",
|
||||
(false, true) if from_nbits > 32 => " on targets with 32-bit wide pointers",
|
||||
_ => return,
|
||||
|
@ -264,8 +264,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
|
||||
}
|
||||
// Local usage
|
||||
} else if let Res::Local(hir_id) = res
|
||||
&& let Some(parent) = get_parent_node(cx.tcx, hir_id)
|
||||
&& let Node::Local(l) = parent
|
||||
&& let Node::Local(l) = get_parent_node(cx.tcx, hir_id)
|
||||
{
|
||||
if let Some(e) = l.init
|
||||
&& is_cast_from_ty_alias(cx, e, cast_from)
|
||||
|
@ -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 = ...; // 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
|
||||
&& 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
|
||||
// traits (identified as local, based on the orphan rule), pessimistically assume that they might
|
||||
// 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
|
||||
&& path_to_local_id(receiver, 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:
|
||||
//
|
||||
// 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(());
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON_INFO,
|
||||
crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO,
|
||||
crate::attrs::DEPRECATED_CFG_ATTR_INFO,
|
||||
crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO,
|
||||
crate::attrs::DEPRECATED_SEMVER_INFO,
|
||||
crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_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::NON_MINIMAL_CFG_INFO,
|
||||
crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO,
|
||||
crate::attrs::UNNECESSARY_CLIPPY_CFG_INFO,
|
||||
crate::attrs::USELESS_ATTRIBUTE_INFO,
|
||||
crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO,
|
||||
crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO,
|
||||
|
@ -1009,7 +1009,7 @@ fn report<'tcx>(
|
||||
state.msg,
|
||||
|diag| {
|
||||
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(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))),
|
||||
_ => (e.precedence().order(), false),
|
||||
|
@ -1,13 +1,16 @@
|
||||
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 rustc_ast::Attribute;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::DiagnosticBuilder;
|
||||
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_session::impl_lint_pass;
|
||||
use rustc_span::{ExpnId, Span};
|
||||
use rustc_span::{ExpnId, MacroKind, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -57,6 +60,10 @@ pub struct DisallowedMacros {
|
||||
conf_disallowed: Vec<DisallowedPath>,
|
||||
disallowed: DefIdMap<usize>,
|
||||
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 {
|
||||
@ -65,10 +72,11 @@ impl DisallowedMacros {
|
||||
conf_disallowed,
|
||||
disallowed: DefIdMap::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() {
|
||||
return;
|
||||
}
|
||||
@ -80,18 +88,26 @@ impl DisallowedMacros {
|
||||
|
||||
if let Some(&index) = self.disallowed.get(&mac.def_id) {
|
||||
let conf = &self.conf_disallowed[index];
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DISALLOWED_MACROS,
|
||||
mac.span,
|
||||
&format!("use of a disallowed macro `{}`", conf.path()),
|
||||
|diag| {
|
||||
if let Some(reason) = conf.reason() {
|
||||
diag.note(reason);
|
||||
}
|
||||
},
|
||||
);
|
||||
let msg = format!("use of a disallowed macro `{}`", conf.path());
|
||||
let add_note = |diag: &mut DiagnosticBuilder<'_, _>| {
|
||||
if let Some(reason) = conf.reason() {
|
||||
diag.note(reason);
|
||||
}
|
||||
};
|
||||
if matches!(mac.kind, MacroKind::Derive)
|
||||
&& let Some(derive_src) = derive_src
|
||||
{
|
||||
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<'_>) {
|
||||
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
|
||||
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<'_>) {
|
||||
self.check(cx, stmt.span);
|
||||
self.check(cx, stmt.span, None);
|
||||
}
|
||||
|
||||
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<'_>) {
|
||||
self.check(cx, pat.span);
|
||||
self.check(cx, pat.span, None);
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
self.check(cx, item.span);
|
||||
self.check(cx, item.vis_span);
|
||||
self.check(cx, item.span, self.derive_src);
|
||||
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<'_>) {
|
||||
self.check(cx, item.span);
|
||||
self.check(cx, item.vis_span);
|
||||
self.check(cx, item.span, None);
|
||||
self.check(cx, item.vis_span, None);
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) {
|
||||
self.check(cx, item.span);
|
||||
self.check(cx, item.vis_span);
|
||||
self.check(cx, item.span, None);
|
||||
self.check(cx, item.vis_span, None);
|
||||
}
|
||||
|
||||
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) {
|
||||
self.check(cx, path.span);
|
||||
self.check(cx, path.span, None);
|
||||
}
|
||||
|
||||
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) {
|
||||
self.check(cx, attr.span);
|
||||
self.check(cx, attr.span, self.derive_src);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) {
|
||||
let parent_node = get_parent_node(cx.tcx, drop_expr.hir_id);
|
||||
if let Some(Node::Arm(Arm { body, .. })) = &parent_node {
|
||||
if let Node::Arm(Arm { body, .. }) = get_parent_node(cx.tcx, drop_expr.hir_id) {
|
||||
return body.hir_id == drop_expr.hir_id;
|
||||
}
|
||||
}
|
||||
|
@ -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::macros::{
|
||||
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::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::Ty;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::edition::Edition::Edition2021;
|
||||
use rustc_span::{sym, Span, Symbol};
|
||||
|
||||
@ -189,32 +188,18 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
||||
&& is_format_macro(cx, macro_call.def_id)
|
||||
&& let Some(format_args) = find_format_args(cx, expr, macro_call.expn)
|
||||
{
|
||||
for piece in &format_args.template {
|
||||
if let FormatArgsPiece::Placeholder(placeholder) = piece
|
||||
&& let Ok(index) = placeholder.argument.index
|
||||
&& let Some(arg) = format_args.arguments.all_args().get(index)
|
||||
{
|
||||
let arg_expr = find_format_arg_expr(expr, arg);
|
||||
let linter = FormatArgsExpr {
|
||||
cx,
|
||||
expr,
|
||||
macro_call: ¯o_call,
|
||||
format_args: &format_args,
|
||||
ignore_mixed: self.ignore_mixed,
|
||||
};
|
||||
|
||||
check_unused_format_specifier(cx, placeholder, arg_expr);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
linter.check_templates();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
fn check_unused_format_specifier(
|
||||
cx: &LateContext<'_>,
|
||||
placeholder: &FormatPlaceholder,
|
||||
arg_expr: Result<&Expr<'_>, &rustc_ast::Expr>,
|
||||
) {
|
||||
let ty_or_ast_expr = arg_expr.map(|expr| cx.typeck_results().expr_ty(expr).peel_refs());
|
||||
struct FormatArgsExpr<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
macro_call: &'a MacroCall,
|
||||
format_args: &'a rustc_ast::FormatArgs,
|
||||
ignore_mixed: bool,
|
||||
}
|
||||
|
||||
let is_format_args = match ty_or_ast_expr {
|
||||
Ok(ty) => is_type_lang_item(cx, ty, LangItem::FormatArguments),
|
||||
Err(expr) => matches!(expr.peel_parens_and_refs().kind, rustc_ast::ExprKind::FormatArgs(_)),
|
||||
};
|
||||
impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
|
||||
fn check_templates(&self) {
|
||||
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 {
|
||||
Ok(expr) => expr.span,
|
||||
Err(expr) => expr.span,
|
||||
};
|
||||
if let Ok(arg_expr) = arg_expr
|
||||
&& placeholder.format_trait == FormatTrait::Display
|
||||
&& 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
|
||||
&& is_format_args
|
||||
&& *options != FormatOptions::default()
|
||||
{
|
||||
span_lint_and_then(
|
||||
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!()`");
|
||||
fn check_unused_format_specifier(
|
||||
&self,
|
||||
placeholder: &FormatPlaceholder,
|
||||
arg_expr: Result<&Expr<'_>, &rustc_ast::Expr>,
|
||||
) {
|
||||
let ty_or_ast_expr = arg_expr.map(|expr| self.cx.typeck_results().expr_ty(expr).peel_refs());
|
||||
|
||||
if let Some(mac_call) = root_macro_call(arg_span)
|
||||
&& cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id)
|
||||
{
|
||||
diag.span_suggestion(
|
||||
cx.sess().source_map().span_until_char(mac_call.span, '!'),
|
||||
message,
|
||||
"format",
|
||||
let is_format_args = match ty_or_ast_expr {
|
||||
Ok(ty) => is_type_lang_item(self.cx, ty, LangItem::FormatArguments),
|
||||
Err(expr) => matches!(expr.peel_parens_and_refs().kind, rustc_ast::ExprKind::FormatArgs(_)),
|
||||
};
|
||||
|
||||
let options = &placeholder.format_options;
|
||||
|
||||
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,
|
||||
);
|
||||
} else {
|
||||
diag.help(message);
|
||||
}
|
||||
};
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if options.width.is_some() {
|
||||
suggest_format("width");
|
||||
}
|
||||
fn check_uninlined_args(&self) {
|
||||
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() {
|
||||
suggest_format("precision");
|
||||
}
|
||||
let mut fixes = Vec::new();
|
||||
// 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) {
|
||||
diag.span_suggestion_verbose(
|
||||
format_span,
|
||||
"if the current behavior is intentional, remove the format specifiers",
|
||||
"",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
if fixes.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
|
||||
// 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(
|
||||
cx: &LateContext<'_>,
|
||||
args: &rustc_ast::FormatArgs,
|
||||
call_site: Span,
|
||||
def_id: DefId,
|
||||
ignore_mixed: bool,
|
||||
) {
|
||||
if args.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
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
|
||||
// non-format
|
||||
return;
|
||||
fn check_one_arg(&self, pos: &FormatArgPosition, usage: FormatParamUsage, fixes: &mut Vec<(Span, String)>) -> bool {
|
||||
let index = pos.index.unwrap();
|
||||
let arg = &self.format_args.arguments.all_args()[index];
|
||||
|
||||
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(self.format_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
|
||||
&& (!self.ignore_mixed || matches!(arg.kind, FormatArgumentKind::Captured(_)))
|
||||
}
|
||||
}
|
||||
|
||||
let mut fixes = Vec::new();
|
||||
// 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 format_arg_positions(args) {
|
||||
if !check_one_arg(args, pos, usage, &mut fixes, ignore_mixed) {
|
||||
fn check_format_in_format_args(&self, 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 !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() {
|
||||
return;
|
||||
}
|
||||
|
||||
// multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308
|
||||
// in those cases, make the code suggestion hidden
|
||||
let multiline_fix = fixes.iter().any(|(span, _)| 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(
|
||||
cx,
|
||||
UNINLINED_FORMAT_ARGS,
|
||||
call_site,
|
||||
"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_one_arg(
|
||||
args: &rustc_ast::FormatArgs,
|
||||
pos: &FormatArgPosition,
|
||||
usage: FormatParamUsage,
|
||||
fixes: &mut Vec<(Span, String)>,
|
||||
ignore_mixed: bool,
|
||||
) -> bool {
|
||||
let index = pos.index.unwrap();
|
||||
let arg = &args.arguments.all_args()[index];
|
||||
|
||||
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 check_to_string_in_format_args(&self, name: Symbol, value: &Expr<'_>) {
|
||||
let cx = self.cx;
|
||||
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(
|
||||
format_args: &rustc_ast::FormatArgs,
|
||||
) -> impl Iterator<Item = (&FormatArgPosition, FormatParamUsage)> {
|
||||
format_args.template.iter().flat_map(|piece| match piece {
|
||||
FormatArgsPiece::Placeholder(placeholder) => {
|
||||
let mut positions = ArrayVec::<_, 3>::new();
|
||||
fn format_arg_positions(&self) -> impl Iterator<Item = (&FormatArgPosition, FormatParamUsage)> {
|
||||
self.format_args.template.iter().flat_map(|piece| match piece {
|
||||
FormatArgsPiece::Placeholder(placeholder) => {
|
||||
let mut positions = ArrayVec::<_, 3>::new();
|
||||
|
||||
positions.push((&placeholder.argument, FormatParamUsage::Argument));
|
||||
if let Some(FormatCount::Argument(position)) = &placeholder.format_options.width {
|
||||
positions.push((position, FormatParamUsage::Width));
|
||||
}
|
||||
if let Some(FormatCount::Argument(position)) = &placeholder.format_options.precision {
|
||||
positions.push((position, FormatParamUsage::Precision));
|
||||
}
|
||||
positions.push((&placeholder.argument, FormatParamUsage::Argument));
|
||||
if let Some(FormatCount::Argument(position)) = &placeholder.format_options.width {
|
||||
positions.push((position, FormatParamUsage::Width));
|
||||
}
|
||||
if let Some(FormatCount::Argument(position)) = &placeholder.format_options.precision {
|
||||
positions.push((position, FormatParamUsage::Precision));
|
||||
}
|
||||
|
||||
positions
|
||||
},
|
||||
FormatArgsPiece::Literal(_) => ArrayVec::new(),
|
||||
})
|
||||
}
|
||||
positions
|
||||
},
|
||||
FormatArgsPiece::Literal(_) => ArrayVec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
format_arg_positions(format_args)
|
||||
.filter(|(position, _)| position.index == Ok(index))
|
||||
.at_most_one()
|
||||
.is_err()
|
||||
/// Returns true if the format argument at `index` is referred to by multiple format params
|
||||
fn is_aliased(&self, index: usize) -> bool {
|
||||
self.format_arg_positions()
|
||||
.filter(|(position, _)| position.index == Ok(index))
|
||||
.at_most_one()
|
||||
.is_err()
|
||||
}
|
||||
}
|
||||
|
||||
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
|
||||
|
@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{sym, Span, Symbol};
|
||||
use rustc_span::{sym, Symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### 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<'_>) {
|
||||
// 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() {
|
||||
self.format_trait_impl = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let Some(format_trait_impl) = self.format_trait_impl else {
|
||||
return;
|
||||
};
|
||||
|
||||
if format_trait_impl.name == sym::Display {
|
||||
check_to_string_in_display(cx, expr);
|
||||
if let Some(format_trait_impl) = self.format_trait_impl {
|
||||
let linter = FormatImplExpr {
|
||||
cx,
|
||||
expr,
|
||||
format_trait_impl,
|
||||
};
|
||||
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<'_>) {
|
||||
if let ExprKind::MethodCall(path, self_arg, ..) = expr.kind
|
||||
// Get the hir_id of the object we are calling the method on
|
||||
// Is the method to_string() ?
|
||||
&& path.ident.name == sym::to_string
|
||||
// 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",
|
||||
);
|
||||
}
|
||||
struct FormatImplExpr<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
format_trait_impl: FormatTraitNames,
|
||||
}
|
||||
|
||||
fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTraitNames) {
|
||||
// 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(cx, expr)
|
||||
&& let macro_def_id = outer_macro.def_id
|
||||
&& is_format_macro(cx, macro_def_id)
|
||||
&& let Some(format_args) = find_format_args(cx, 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),
|
||||
impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> {
|
||||
fn check_to_string_in_display(&self) {
|
||||
if self.format_trait_impl.name == sym::Display
|
||||
&& let ExprKind::MethodCall(path, self_arg, ..) = self.expr.kind
|
||||
// Get the hir_id of the object we are calling the method on
|
||||
// Is the method to_string() ?
|
||||
&& path.ident.name == sym::to_string
|
||||
// Is the method a part of the ToString trait? (i.e. not to_string() implemented
|
||||
// separately)
|
||||
&& let Some(expr_def_id) = self.cx.typeck_results().type_dependent_def_id(self.expr.hir_id)
|
||||
&& is_diag_trait_item(self.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(
|
||||
self.cx,
|
||||
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) {
|
||||
// Handle multiple dereferencing of references e.g. &&self
|
||||
// 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
|
||||
let reference = peel_ref_operators(cx, arg);
|
||||
let map = cx.tcx.hir();
|
||||
// Is the reference self?
|
||||
if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
|
||||
let FormatTraitNames { name, .. } = impl_trait;
|
||||
span_lint(
|
||||
cx,
|
||||
RECURSIVE_FORMAT_IMPL,
|
||||
span,
|
||||
&format!("using `self` as `{name}` in `impl {name}` will cause infinite recursion"),
|
||||
);
|
||||
fn check_format_arg_self(&self, arg: &Expr<'_>) {
|
||||
// Handle multiple dereferencing of references e.g. &&self
|
||||
// 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
|
||||
let reference = peel_ref_operators(self.cx, arg);
|
||||
let map = self.cx.tcx.hir();
|
||||
// Is the reference self?
|
||||
if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
|
||||
let FormatTraitNames { name, .. } = self.format_trait_impl;
|
||||
span_lint(
|
||||
self.cx,
|
||||
RECURSIVE_FORMAT_IMPL,
|
||||
self.expr.span,
|
||||
&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) {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||
&& let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id)
|
||||
{
|
||||
let replacement = match name {
|
||||
sym::print_macro | sym::eprint_macro => "write",
|
||||
sym::println_macro | sym::eprintln_macro => "writeln",
|
||||
_ => return,
|
||||
};
|
||||
fn check_print_in_format_impl(&self) {
|
||||
if let Some(macro_call) = root_macro_call_first_node(self.cx, self.expr)
|
||||
&& let Some(name) = self.cx.tcx.get_diagnostic_name(macro_call.def_id)
|
||||
{
|
||||
let replacement = match name {
|
||||
sym::print_macro | sym::eprint_macro => "write",
|
||||
sym::println_macro | sym::eprintln_macro => "writeln",
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let name = name.as_str().strip_suffix("_macro").unwrap();
|
||||
let name = name.as_str().strip_suffix("_macro").unwrap();
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PRINT_IN_FORMAT_IMPL,
|
||||
macro_call.span,
|
||||
&format!("use of `{name}!` in `{}` impl", impl_trait.name),
|
||||
"replace with",
|
||||
if let Some(formatter_name) = impl_trait.formatter_name {
|
||||
format!("{replacement}!({formatter_name}, ..)")
|
||||
} else {
|
||||
format!("{replacement}!(..)")
|
||||
},
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
span_lint_and_sugg(
|
||||
self.cx,
|
||||
PRINT_IN_FORMAT_IMPL,
|
||||
macro_call.span,
|
||||
&format!("use of `{name}!` in `{}` impl", self.format_trait_impl.name),
|
||||
"replace with",
|
||||
if let Some(formatter_name) = self.format_trait_impl.formatter_name {
|
||||
format!("{replacement}!({formatter_name}, ..)")
|
||||
} else {
|
||||
format!("{replacement}!(..)")
|
||||
},
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,6 +181,9 @@ fn convert_to_from(
|
||||
let from = snippet_opt(cx, self_ty.span)?;
|
||||
let into = snippet_opt(cx, target_ty.span)?;
|
||||
|
||||
let return_type = matches!(sig.decl.output, FnRetTy::Return(_))
|
||||
.then_some(String::from("Self"))
|
||||
.unwrap_or_default();
|
||||
let mut suggestions = vec![
|
||||
// impl Into<T> for U -> impl From<T> for U
|
||||
// ~~~~ ~~~~
|
||||
@ -197,13 +200,10 @@ fn convert_to_from(
|
||||
// fn into([mut] self) -> T -> fn into([mut] v: T) -> T
|
||||
// ~~~~ ~~~~
|
||||
(self_ident.span, format!("val: {from}")),
|
||||
];
|
||||
|
||||
if let FnRetTy::Return(_) = sig.decl.output {
|
||||
// fn into(self) -> T -> fn into(self) -> Self
|
||||
// ~ ~~~~
|
||||
suggestions.push((sig.decl.output.span(), String::from("Self")));
|
||||
}
|
||||
(sig.decl.output.span(), return_type),
|
||||
];
|
||||
|
||||
let mut finder = SelfFinder {
|
||||
cx,
|
||||
|
@ -1,16 +1,17 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
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::{
|
||||
Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, TraitBoundModifier, TraitItem,
|
||||
TraitItemKind, TyKind,
|
||||
Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, OpaqueTy, TraitBoundModifier,
|
||||
TraitItem, TraitItemKind, TyKind, TypeBinding,
|
||||
};
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -50,20 +51,17 @@ declare_clippy_lint! {
|
||||
}
|
||||
declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]);
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn emit_lint(
|
||||
cx: &LateContext<'_>,
|
||||
poly_trait: &rustc_hir::PolyTraitRef<'_>,
|
||||
opaque_ty: &rustc_hir::OpaqueTy<'_>,
|
||||
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<'_>],
|
||||
// The original bindings that `implied_bindings` are implied from
|
||||
implied_by_bindings: &[rustc_hir::TypeBinding<'_>],
|
||||
implied_by_args: &[GenericArg<'_>],
|
||||
implied_by_span: Span,
|
||||
bound: &ImplTraitBound<'_>,
|
||||
) {
|
||||
let implied_by = snippet(cx, implied_by_span, "..");
|
||||
let implied_by = snippet(cx, bound.span, "..");
|
||||
|
||||
span_lint_and_then(
|
||||
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`
|
||||
let omitted_assoc_tys: Vec<_> = implied_bindings
|
||||
.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();
|
||||
|
||||
if !omitted_assoc_tys.is_empty() {
|
||||
// `<>` 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 insert_span = match (implied_by_args, implied_by_bindings) {
|
||||
let needs_angle_brackets = bound.args.is_empty() && bound.bindings.is_empty();
|
||||
let insert_span = match (bound.args, bound.bindings) {
|
||||
([.., arg], [.., binding]) => arg.span().max(binding.span).shrink_to_hi(),
|
||||
([.., arg], []) => arg.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 {
|
||||
@ -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<'_>) {
|
||||
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 ItemKind::OpaqueTy(opaque_ty) = item.kind
|
||||
// Very often there is only a single bound, e.g. `impl Deref<..>`, in which case
|
||||
// we can avoid doing a bunch of stuff unnecessarily.
|
||||
&& opaque_ty.bounds.len() > 1
|
||||
{
|
||||
// Get all the (implied) trait predicates in the bounds.
|
||||
// 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();
|
||||
let supertraits = collect_supertrait_bounds(cx, opaque_ty);
|
||||
|
||||
// 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
|
||||
// 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() {
|
||||
@ -267,42 +316,18 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
|
||||
&& 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 Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
|
||||
&& let Some((implied_by_span, implied_by_args, implied_by_bindings)) =
|
||||
implied_bounds
|
||||
.iter()
|
||||
.find_map(|&(span, implied_by_path, preds, implied_by_def_id)| {
|
||||
let implied_by_args = implied_by_path.args.map_or([].as_slice(), |a| a.args);
|
||||
let implied_by_bindings = implied_by_path.args.map_or([].as_slice(), |a| a.bindings);
|
||||
|
||||
preds.iter().find_map(|(clause, _)| {
|
||||
if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
|
||||
&& 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
|
||||
}
|
||||
})
|
||||
})
|
||||
&& let Some(bound) = find_bound_in_supertraits(cx, def_id, implied_args, &supertraits)
|
||||
// If the implied bound has a type binding that also exists in the implied-by trait,
|
||||
// then we shouldn't lint. See #11880 for an example.
|
||||
&& let assocs = cx.tcx.associated_items(bound.trait_def_id)
|
||||
&& !implied_bindings.iter().any(|binding| {
|
||||
assocs
|
||||
.filter_by_name_unhygienic(binding.ident.name)
|
||||
.next()
|
||||
.is_some_and(|assoc| assoc.kind == ty::AssocKind::Type)
|
||||
})
|
||||
{
|
||||
emit_lint(
|
||||
cx,
|
||||
poly_trait,
|
||||
opaque_ty,
|
||||
index,
|
||||
implied_bindings,
|
||||
implied_by_bindings,
|
||||
implied_by_args,
|
||||
implied_by_span,
|
||||
);
|
||||
emit_lint(cx, poly_trait, opaque_ty, index, implied_bindings, bound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
use clippy_config::msrvs::Msrv;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_in_test_function;
|
||||
use rustc_attr::{StabilityLevel, StableSince};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_hir::{Expr, ExprKind, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{ExpnKind, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -81,13 +82,18 @@ impl IncompatibleMsrv {
|
||||
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() {
|
||||
// We don't check local items since their MSRV is supposed to always be valid.
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
self.emit_lint_for(cx, span, version);
|
||||
@ -117,14 +123,14 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv {
|
||||
match expr.kind {
|
||||
ExprKind::MethodCall(_, _, _, span) => {
|
||||
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, [_]) => {
|
||||
if let ExprKind::Path(qpath) = call.kind
|
||||
&& 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);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
|
@ -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::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;
|
||||
}
|
||||
|
||||
|
@ -174,6 +174,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
|
||||
// only `usize` index is legal in rust array index
|
||||
// leave other type to rustc
|
||||
if let Constant::Int(off) = constant
|
||||
&& off <= usize::MAX as u128
|
||||
&& let ty::Uint(utype) = cx.typeck_results().expr_ty(index).kind()
|
||||
&& *utype == ty::UintTy::Usize
|
||||
&& let ty::Array(_, s) = ty.kind()
|
||||
|
@ -385,7 +385,6 @@ impl LateLintPass<'_> for ItemNameRepetitions {
|
||||
assert!(last.is_some());
|
||||
}
|
||||
|
||||
#[expect(clippy::similar_names)]
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
let item_name = item.ident.name.as_str();
|
||||
let item_camel = to_camel_case(item_name);
|
||||
|
@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator {
|
||||
if matches!(name, "iter" | "iter_mut")
|
||||
&& !matches!(
|
||||
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 {
|
||||
|
@ -14,7 +14,7 @@
|
||||
clippy::missing_docs_in_private_items,
|
||||
clippy::must_use_candidate,
|
||||
rustc::diagnostic_outside_of_impl,
|
||||
rustc::untranslatable_diagnostic,
|
||||
rustc::untranslatable_diagnostic
|
||||
)]
|
||||
#![warn(trivial_casts, trivial_numeric_casts)]
|
||||
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
||||
|
@ -1,17 +1,18 @@
|
||||
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::{Expr, ExprKind, FnRetTy, FnSig, Node};
|
||||
use rustc_ast::Label;
|
||||
use rustc_errors::Applicability;
|
||||
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;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &Expr<'_>,
|
||||
expr: &Expr<'tcx>,
|
||||
loop_block: &'tcx hir::Block<'_>,
|
||||
label: Option<Label>,
|
||||
) {
|
||||
@ -34,6 +35,10 @@ pub(super) fn check<'tcx>(
|
||||
return;
|
||||
}
|
||||
|
||||
if in_external_macro(cx.sess(), expr.span) || is_from_proc_macro(cx, expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut loop_visitor = LoopVisitor {
|
||||
cx,
|
||||
label,
|
||||
|
@ -36,7 +36,8 @@ struct PathAndSpan {
|
||||
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)]
|
||||
pub struct MacroRefData {
|
||||
name: String,
|
||||
|
@ -64,38 +64,50 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
||||
let min_index = usize::min(lindex, rindex);
|
||||
let max_index = usize::max(lindex, rindex);
|
||||
|
||||
let mut local_map: HirIdMap<HirId> = HirIdMap::default();
|
||||
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
|
||||
if let Some(a_id) = path_to_local(a)
|
||||
&& let Some(b_id) = path_to_local(b)
|
||||
&& let entry = match local_map.entry(a_id) {
|
||||
HirIdMapEntry::Vacant(entry) => entry,
|
||||
// check if using the same bindings as before
|
||||
HirIdMapEntry::Occupied(entry) => return *entry.get() == b_id,
|
||||
}
|
||||
let check_eq_with_pat = |expr_a: &Expr<'_>, expr_b: &Expr<'_>| {
|
||||
let mut local_map: HirIdMap<HirId> = HirIdMap::default();
|
||||
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
|
||||
if let Some(a_id) = path_to_local(a)
|
||||
&& let Some(b_id) = path_to_local(b)
|
||||
&& let entry = match local_map.entry(a_id) {
|
||||
HirIdMapEntry::Vacant(entry) => entry,
|
||||
// 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
|
||||
&& cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id)
|
||||
&& cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b)
|
||||
&& pat_contains_local(lhs.pat, a_id)
|
||||
&& pat_contains_local(rhs.pat, b_id)
|
||||
{
|
||||
entry.insert(b_id);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
// Arms with a guard are ignored, those can’t 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)
|
||||
&& lhs.guard.is_none()
|
||||
&& rhs.guard.is_none()
|
||||
&& SpanlessEq::new(cx)
|
||||
.expr_fallback(eq_fallback)
|
||||
.eq_expr(lhs.body, rhs.body)
|
||||
&& cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b)
|
||||
&& pat_contains_local(lhs.pat, a_id)
|
||||
&& pat_contains_local(rhs.pat, b_id)
|
||||
{
|
||||
entry.insert(b_id);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
SpanlessEq::new(cx)
|
||||
.expr_fallback(eq_fallback)
|
||||
.eq_expr(expr_a, expr_b)
|
||||
// these checks could be removed to allow unused bindings
|
||||
&& bindings_eq(lhs.pat, local_map.keys().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 can’t 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();
|
||||
|
@ -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
|
||||
/// 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 {
|
||||
if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) {
|
||||
match p_node {
|
||||
// Compare match_expr ty with local in `let local = match match_expr {..}`
|
||||
Node::Local(local) => {
|
||||
let results = cx.typeck_results();
|
||||
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`
|
||||
Node::Item(item) => {
|
||||
if let ItemKind::Fn(..) = item.kind {
|
||||
let output = cx
|
||||
.tcx
|
||||
.fn_sig(item.owner_id)
|
||||
.instantiate_identity()
|
||||
.output()
|
||||
.skip_binder();
|
||||
return same_type_and_consts(output, cx.typeck_results().expr_ty(expr));
|
||||
}
|
||||
},
|
||||
// check the parent expr for this whole block `{ match match_expr {..} }`
|
||||
Node::Block(block) => {
|
||||
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);
|
||||
}
|
||||
},
|
||||
// recursively call on `if xxx {..}` etc.
|
||||
Node::Expr(p_expr) => {
|
||||
return expr_ty_matches_p_ty(cx, expr, p_expr);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
match get_parent_node(cx.tcx, p_expr.hir_id) {
|
||||
// Compare match_expr ty with local in `let local = match match_expr {..}`
|
||||
Node::Local(local) => {
|
||||
let results = cx.typeck_results();
|
||||
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`
|
||||
Node::Item(item) => {
|
||||
if let ItemKind::Fn(..) = item.kind {
|
||||
let output = cx
|
||||
.tcx
|
||||
.fn_sig(item.owner_id)
|
||||
.instantiate_identity()
|
||||
.output()
|
||||
.skip_binder();
|
||||
return same_type_and_consts(output, cx.typeck_results().expr_ty(expr));
|
||||
}
|
||||
},
|
||||
// check the parent expr for this whole block `{ match match_expr {..} }`
|
||||
Node::Block(block) => {
|
||||
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);
|
||||
}
|
||||
},
|
||||
// recursively call on `if xxx {..}` etc.
|
||||
Node::Expr(p_expr) => {
|
||||
return expr_ty_matches_p_ty(cx, expr, p_expr);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
false
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use clippy_utils::visitors::{for_each_expr, is_local_used};
|
||||
use rustc_ast::{BorrowKind, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
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_span::symbol::Ident;
|
||||
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(..), ..),
|
||||
)
|
||||
},
|
||||
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,
|
||||
_ => false,
|
||||
} {
|
||||
|
@ -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 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.
|
||||
|
@ -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::sugg::Sugg;
|
||||
use clippy_utils::ty::is_non_aggregate_primitive_type;
|
||||
use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators, 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_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
@ -232,7 +234,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
|
||||
// Check that second argument is `Option::None`
|
||||
if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
|
||||
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_uninit(cx, src, dest, expr.span);
|
||||
|
@ -38,6 +38,7 @@ pub(super) fn check<'tcx>(
|
||||
&& 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_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()
|
||||
&& (recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String))
|
||||
{
|
||||
|
@ -49,7 +49,7 @@ pub(super) fn check(
|
||||
|
||||
if is_copy(cx, ty) {
|
||||
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
|
||||
ExprKind::AddrOf(..) => return,
|
||||
// (*x).func() is useless, x.clone().func() can work in case func borrows self
|
||||
@ -70,7 +70,7 @@ pub(super) fn check(
|
||||
_ => false,
|
||||
},
|
||||
// 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;
|
||||
},
|
||||
_ => false,
|
||||
|
@ -28,104 +28,102 @@ pub(super) fn check<'tcx>(
|
||||
iter_expr: &'tcx Expr<'tcx>,
|
||||
call_span: Span,
|
||||
) {
|
||||
if let Some(parent) = get_parent_node(cx.tcx, collect_expr.hir_id) {
|
||||
match parent {
|
||||
Node::Expr(parent) => {
|
||||
check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr);
|
||||
match get_parent_node(cx.tcx, collect_expr.hir_id) {
|
||||
Node::Expr(parent) => {
|
||||
check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr);
|
||||
|
||||
if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let name = name.ident.as_str();
|
||||
let collect_ty = cx.typeck_results().expr_ty(collect_expr);
|
||||
if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let name = name.ident.as_str();
|
||||
let collect_ty = cx.typeck_results().expr_ty(collect_expr);
|
||||
|
||||
let sugg: String = match name {
|
||||
"len" => {
|
||||
if let Some(adt) = collect_ty.ty_adt_def()
|
||||
&& matches!(
|
||||
cx.tcx.get_diagnostic_name(adt.did()),
|
||||
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) =>
|
||||
let sugg: String = match name {
|
||||
"len" => {
|
||||
if let Some(adt) = collect_ty.ty_adt_def()
|
||||
&& matches!(
|
||||
cx.tcx.get_diagnostic_name(adt.did()),
|
||||
Some(sym::Vec | sym::VecDeque | sym::LinkedList | sym::BinaryHeap)
|
||||
)
|
||||
{
|
||||
"next().is_none()".into()
|
||||
},
|
||||
"contains" => {
|
||||
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 {
|
||||
(arg.span, "*")
|
||||
};
|
||||
let snip = snippet_with_applicability(cx, span, "??", &mut app);
|
||||
format!("any(|x| x == {prefix}{snip})")
|
||||
"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()
|
||||
},
|
||||
"contains" => {
|
||||
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 {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
(arg.span, "*")
|
||||
};
|
||||
let snip = snippet_with_applicability(cx, span, "??", &mut app);
|
||||
format!("any(|x| x == {prefix}{snip})")
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_COLLECT,
|
||||
call_span.with_hi(parent.span.hi()),
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
"replace with",
|
||||
sugg,
|
||||
app,
|
||||
);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_COLLECT,
|
||||
call_span.with_hi(parent.span.hi()),
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
"replace with",
|
||||
sugg,
|
||||
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
|
||||
let mut span = MultiSpan::from_span(name_span);
|
||||
span.push_span_label(iter_call.span, "the iterator could be used here instead");
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
super::NEEDLESS_COLLECT,
|
||||
collect_expr.hir_id,
|
||||
span,
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
|diag| {
|
||||
let iter_replacement =
|
||||
format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx));
|
||||
diag.multipart_suggestion(
|
||||
iter_call.get_suggestion_text(),
|
||||
vec![(l.span, String::new()), (iter_call.span, iter_replacement)],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
// Suggest replacing iter_call with iter_replacement, and removing stmt
|
||||
let mut span = MultiSpan::from_span(name_span);
|
||||
span.push_span_label(iter_call.span, "the iterator could be used here instead");
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
super::NEEDLESS_COLLECT,
|
||||
collect_expr.hir_id,
|
||||
span,
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
|diag| {
|
||||
let iter_replacement =
|
||||
format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx));
|
||||
diag.multipart_suggestion(
|
||||
iter_call.get_suggestion_text(),
|
||||
vec![(l.span, String::new()), (iter_call.span, iter_replacement)],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
@ -10,17 +10,71 @@ use rustc_span::{sym, Span};
|
||||
|
||||
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
|
||||
/// call
|
||||
#[derive(Copy, Clone)]
|
||||
#[expect(clippy::enum_variant_names)]
|
||||
enum FunctionKind {
|
||||
/// `T::try_from(U)`
|
||||
TryFromFunction,
|
||||
TryFromFunction(Option<SpansKind>),
|
||||
/// `t.try_into()`
|
||||
TryIntoMethod,
|
||||
/// `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>(
|
||||
@ -35,8 +89,8 @@ fn check<'tcx>(
|
||||
&& self_ty != other_ty
|
||||
&& let Some(self_ty) = self_ty.as_type()
|
||||
&& let Some(from_into_trait) = cx.tcx.get_diagnostic_item(match kind {
|
||||
FunctionKind::TryFromFunction => sym::From,
|
||||
FunctionKind::TryIntoMethod | FunctionKind::TryIntoFunction => sym::Into,
|
||||
FunctionKind::TryFromFunction(_) => sym::From,
|
||||
FunctionKind::TryIntoMethod | FunctionKind::TryIntoFunction(_) => sym::Into,
|
||||
})
|
||||
// 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
|
||||
@ -45,49 +99,37 @@ fn check<'tcx>(
|
||||
&& implements_trait(cx, self_ty, from_into_trait, &[other_ty])
|
||||
&& 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| {
|
||||
if let ExprKind::MethodCall(path, .., span) = parent.kind
|
||||
&& let sym::unwrap | sym::expect = path.ident.name
|
||||
{
|
||||
Some(span)
|
||||
// include `.` before `unwrap`/`expect`
|
||||
Some(span.with_lo(expr.span.hi()))
|
||||
} else {
|
||||
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
|
||||
|
||||
(
|
||||
self_ty,
|
||||
other_ty,
|
||||
"into()",
|
||||
primary_span.with_hi(unwrap_span.hi()),
|
||||
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),
|
||||
// If there is an unwrap/expect call, extend the span to include the call
|
||||
let span = if let Some(unwrap_call) = parent_unwrap_call {
|
||||
primary_span.with_hi(unwrap_call.hi())
|
||||
} else {
|
||||
primary_span
|
||||
};
|
||||
|
||||
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(
|
||||
cx,
|
||||
UNNECESSARY_FALLIBLE_CONVERSIONS,
|
||||
@ -97,7 +139,7 @@ fn check<'tcx>(
|
||||
with_forced_trimmed_paths!({
|
||||
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(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(
|
||||
cx,
|
||||
expr,
|
||||
cx.typeck_results().node_args(callee.hir_id),
|
||||
match cx.tcx.get_diagnostic_name(trait_def_id) {
|
||||
Some(sym::TryFrom) => FunctionKind::TryFromFunction,
|
||||
Some(sym::TryInto) => FunctionKind::TryIntoFunction,
|
||||
Some(sym::TryFrom) => FunctionKind::TryFromFunction(qpath_spans),
|
||||
Some(sym::TryInto) => FunctionKind::TryIntoFunction(qpath_spans),
|
||||
_ => return,
|
||||
},
|
||||
callee.span,
|
||||
|
@ -5,7 +5,8 @@ use clippy_utils::ty::implements_trait;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, GenericArgKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::iter;
|
||||
|
@ -3,7 +3,9 @@ use super::unnecessary_iter_cloned::{self, is_into_iter};
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
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::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
|
||||
use rustc_errors::Applicability;
|
||||
@ -16,7 +18,8 @@ use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
|
||||
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_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);
|
||||
}
|
||||
} else {
|
||||
check_borrow_predicate(cx, expr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -590,3 +595,92 @@ fn is_to_string_on_string_like<'a>(
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::intravisit::{walk_item, Visitor};
|
||||
use rustc_hir::{GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind};
|
||||
use rustc_hir::intravisit::{walk_item, walk_trait_item, Visitor};
|
||||
use rustc_hir::{GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, TraitItem, UsePath};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::impl_lint_pass;
|
||||
@ -53,7 +53,7 @@ impl MinIdentChars {
|
||||
&& str.len() <= self.min_ident_chars_threshold as usize
|
||||
&& !str.starts_with('_')
|
||||
&& !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);
|
||||
}
|
||||
|
||||
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`. :/
|
||||
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
|
||||
if let PatKind::Binding(_, _, ident, ..) = pat.kind
|
||||
@ -105,11 +113,26 @@ impl Visitor<'_> for IdentVisitor<'_, '_> {
|
||||
|
||||
let str = ident.as_str();
|
||||
if conf.is_ident_too_short(cx, str, ident.span) {
|
||||
if let Node::Item(item) = node
|
||||
&& let ItemKind::Use(..) = item.kind
|
||||
// Check whether the node is part of a `use` statement. We don't want to emit a warning if the user
|
||||
// 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;
|
||||
}
|
||||
|
||||
// `struct Awa<T>(T)`
|
||||
// ^
|
||||
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);
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
|
@ -96,10 +96,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::If(..) => {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||
if adj
|
||||
|
@ -139,7 +139,7 @@ fn condition_needs_parentheses(e: &Expr<'_>) -> bool {
|
||||
fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
|
||||
matches!(
|
||||
get_parent_node(cx.tcx, id),
|
||||
Some(Node::Stmt(..) | Node::Block(Block { stmts: &[], .. }))
|
||||
Node::Stmt(..) | Node::Block(Block { stmts: &[], .. })
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -236,11 +236,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
// #11182; do not lint if mutability is required elsewhere
|
||||
if let ExprKind::Path(..) = expr.kind
|
||||
&& let Some(parent) = get_parent_node(cx.tcx, expr.hir_id)
|
||||
&& let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(expr).kind()
|
||||
&& let Some(def_id) = def_id.as_local()
|
||||
{
|
||||
if let Node::Expr(e) = parent
|
||||
if let Node::Expr(e) = get_parent_node(cx.tcx, expr.hir_id)
|
||||
&& let ExprKind::Call(call, _) = e.kind
|
||||
&& call.hir_id == expr.hir_id
|
||||
{
|
||||
|
@ -75,10 +75,6 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
|
||||
if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
|
||||
let name = impl_item.ident.name;
|
||||
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 {
|
||||
// can't be implemented for unsafe new
|
||||
return;
|
||||
|
@ -5,8 +5,8 @@ use clippy_utils::{any_parent_is_automatically_derived, get_parent_node, is_lint
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{
|
||||
is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, Node, PatKind, Stmt,
|
||||
StmtKind, UnsafeSource,
|
||||
is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind,
|
||||
Stmt, StmtKind, UnsafeSource,
|
||||
};
|
||||
use rustc_infer::infer::TyCtxtInferExt as _;
|
||||
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
|
||||
/// 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
|
||||
/// ```rust,ignore
|
||||
/// let _i_serve_no_purpose = 1;
|
||||
@ -144,7 +140,7 @@ impl NoEffect {
|
||||
for parent in cx.tcx.hir().parent_iter(stmt.hir_id) {
|
||||
if let Node::Item(item) = parent.1
|
||||
&& let ItemKind::Fn(..) = item.kind
|
||||
&& let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id)
|
||||
&& let Node::Block(block) = get_parent_node(cx.tcx, stmt.hir_id)
|
||||
&& let [.., final_stmt] = block.stmts
|
||||
&& final_stmt.hir_id == stmt.hir_id
|
||||
{
|
||||
@ -180,6 +176,7 @@ impl NoEffect {
|
||||
}
|
||||
} else if let StmtKind::Local(local) = stmt.kind {
|
||||
if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id)
|
||||
&& !matches!(local.source, LocalSource::AsyncFn)
|
||||
&& let Some(init) = local.init
|
||||
&& local.els.is_none()
|
||||
&& !local.pat.span.from_expansion()
|
||||
|
@ -112,7 +112,7 @@ declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL
|
||||
impl LateLintPass<'_> for NonCanonicalImpls {
|
||||
#[expect(clippy::too_many_lines)]
|
||||
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;
|
||||
};
|
||||
let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) else {
|
||||
|
@ -119,7 +119,6 @@ impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
|
||||
|
||||
// this list contains lists of names that are allowed to be similar
|
||||
// the assumption is that no name is ever contained in multiple lists.
|
||||
#[rustfmt::skip]
|
||||
const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[
|
||||
&["parsed", "parser"],
|
||||
&["lhs", "rhs"],
|
||||
@ -132,6 +131,14 @@ const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[
|
||||
&["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>);
|
||||
|
||||
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) {
|
||||
let interned_name = ident.name.as_str();
|
||||
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) {
|
||||
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 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
|
||||
{
|
||||
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 existing_str = existing_name.interned.as_str();
|
||||
|
||||
// The first char being different is usually enough to set identifiers apart, as long
|
||||
// as the characters aren't too similar.
|
||||
if !chars_are_similar(
|
||||
interned_name.chars().next().expect("len >= 1"),
|
||||
existing_str.chars().next().expect("len >= 1"),
|
||||
) {
|
||||
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(
|
||||
self.0.cx,
|
||||
SIMILAR_NAMES,
|
||||
@ -302,6 +265,57 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
|
||||
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> {
|
||||
|
@ -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.
|
||||
// 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) {
|
||||
Some(Node::Item(i)) => (i.owner_id.to_def_id(), FnKind::Fn, 0),
|
||||
Some(Node::TraitItem(&TraitItem {
|
||||
Node::Item(i) => (i.owner_id.to_def_id(), FnKind::Fn, 0),
|
||||
Node::TraitItem(&TraitItem {
|
||||
kind: TraitItemKind::Fn(ref sig, _),
|
||||
owner_id,
|
||||
..
|
||||
})) => (
|
||||
}) => (
|
||||
owner_id.to_def_id(),
|
||||
FnKind::TraitFn,
|
||||
usize::from(sig.decl.implicit_self.has_implicit_self()),
|
||||
),
|
||||
Some(Node::ImplItem(&ImplItem {
|
||||
Node::ImplItem(&ImplItem {
|
||||
kind: ImplItemKind::Fn(ref sig, _),
|
||||
owner_id,
|
||||
..
|
||||
})) => {
|
||||
}) => {
|
||||
#[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
|
||||
.tcx
|
||||
.impl_trait_ref(item.owner_id)
|
||||
|
@ -8,7 +8,6 @@ use rustc_span::Span;
|
||||
|
||||
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) {
|
||||
let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) {
|
||||
(ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => {
|
||||
|
@ -11,7 +11,7 @@ use rustc_middle::ty::{self, Ty};
|
||||
|
||||
use super::OP_REF;
|
||||
|
||||
#[expect(clippy::similar_names, clippy::too_many_lines)]
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub(crate) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use clippy_config::types::PubUnderscoreFieldsBehaviour;
|
||||
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 rustc_hir::{FieldDef, Item, ItemKind, LangItem};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
@ -69,13 +69,15 @@ impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields {
|
||||
// We ignore fields that are `PhantomData`.
|
||||
&& !is_path_lang_item(cx, field.ty, LangItem::PhantomData)
|
||||
{
|
||||
span_lint_and_help(
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
PUB_UNDERSCORE_FIELDS,
|
||||
field.hir_id,
|
||||
field.vis_span.to(field.ident.span),
|
||||
"field marked as public but also inferred as unused because it's prefixed with `_`",
|
||||
None,
|
||||
"consider removing the underscore, or making the field private",
|
||||
|diag| {
|
||||
diag.help("consider removing the underscore, or making the field private");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -289,7 +289,7 @@ impl QuestionMark {
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
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!(
|
||||
"{receiver_str}{}?{}",
|
||||
if by_ref == ByRef::Yes { ".as_ref()" } else { "" },
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::rustc_lint::LintContext;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{get_parent_expr, get_parent_node};
|
||||
use hir::Param;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
@ -200,11 +200,11 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
|
||||
hint = hint.asyncify();
|
||||
}
|
||||
|
||||
let is_in_fn_call_arg =
|
||||
clippy_utils::get_parent_node(cx.tcx, expr.hir_id).is_some_and(|x| match x {
|
||||
Node::Expr(expr) => matches!(expr.kind, hir::ExprKind::Call(_, _)),
|
||||
_ => false,
|
||||
});
|
||||
let is_in_fn_call_arg = if let Node::Expr(expr) = get_parent_node(cx.tcx, expr.hir_id) {
|
||||
matches!(expr.kind, hir::ExprKind::Call(_, _))
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// avoid clippy::double_parens
|
||||
if !is_in_fn_call_arg {
|
||||
|
@ -47,7 +47,6 @@ struct ExistingName {
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
|
||||
let mut map = FxHashMap::<Res, ExistingName>::default();
|
||||
|
||||
@ -75,24 +74,23 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
|
||||
|
||||
match of_trait {
|
||||
Some(trait_ref) => {
|
||||
let mut methods_in_trait: BTreeSet<Symbol> =
|
||||
if let Node::TraitRef(TraitRef { path, .. }) =
|
||||
cx.tcx.hir_node(trait_ref.hir_ref_id)
|
||||
&& let Res::Def(DefKind::Trait, did) = path.res
|
||||
{
|
||||
// FIXME: if
|
||||
// `rustc_middle::ty::assoc::AssocItems::items` is public,
|
||||
// we can iterate its keys instead of `in_definition_order`,
|
||||
// which's more efficient
|
||||
cx.tcx
|
||||
.associated_items(did)
|
||||
.in_definition_order()
|
||||
.filter(|assoc_item| matches!(assoc_item.kind, AssocKind::Fn))
|
||||
.map(|assoc_item| assoc_item.name)
|
||||
.collect()
|
||||
} else {
|
||||
BTreeSet::new()
|
||||
};
|
||||
let mut methods_in_trait: BTreeSet<Symbol> = if let Node::TraitRef(TraitRef { path, .. }) =
|
||||
cx.tcx.hir_node(trait_ref.hir_ref_id)
|
||||
&& let Res::Def(DefKind::Trait, did) = path.res
|
||||
{
|
||||
// FIXME: if
|
||||
// `rustc_middle::ty::assoc::AssocItems::items` is public,
|
||||
// we can iterate its keys instead of `in_definition_order`,
|
||||
// which's more efficient
|
||||
cx.tcx
|
||||
.associated_items(did)
|
||||
.in_definition_order()
|
||||
.filter(|assoc_item| matches!(assoc_item.kind, AssocKind::Fn))
|
||||
.map(|assoc_item| assoc_item.name)
|
||||
.collect()
|
||||
} else {
|
||||
BTreeSet::new()
|
||||
};
|
||||
|
||||
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) {
|
||||
|
@ -51,11 +51,11 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings {
|
||||
{
|
||||
let ctxt = expr.span.ctxt();
|
||||
let span = match get_parent_node(cx.tcx, expr.hir_id) {
|
||||
Some(Node::Block(&Block {
|
||||
Node::Block(&Block {
|
||||
rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
|
||||
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,
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use rustc_hir::{Impl, Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
@ -53,6 +54,8 @@ impl<'tcx> LateLintPass<'tcx> for ToStringTraitImpl {
|
||||
}) = it.kind
|
||||
&& let Some(trait_did) = trait_ref.trait_def_id()
|
||||
&& 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(
|
||||
cx,
|
||||
|
@ -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 {
|
||||
return false;
|
||||
};
|
||||
let local_parents = locals
|
||||
.iter()
|
||||
.map(|l| cx.tcx.parent_hir_node(*l))
|
||||
.collect::<Vec<_>>();
|
||||
let local_parents = locals.iter().map(|l| cx.tcx.parent_hir_node(*l)).collect::<Vec<_>>();
|
||||
|
||||
local_parents
|
||||
.iter()
|
||||
|
@ -340,47 +340,43 @@ fn block_parents_have_safety_comment(
|
||||
cx: &LateContext<'_>,
|
||||
id: hir::HirId,
|
||||
) -> bool {
|
||||
if let Some(node) = get_parent_node(cx.tcx, id) {
|
||||
let (span, hir_id) = match node {
|
||||
Node::Expr(expr) => match get_parent_node(cx.tcx, expr.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),
|
||||
let (span, hir_id) = match get_parent_node(cx.tcx, id) {
|
||||
Node::Expr(expr) => match get_parent_node(cx.tcx, expr.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)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
_ => {
|
||||
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 {
|
||||
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.
|
||||
@ -449,53 +445,49 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf
|
||||
if item.span.ctxt() != SyntaxContext::root() {
|
||||
return HasSafetyComment::No;
|
||||
}
|
||||
if let Some(parent_node) = 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::Item(parent_item) => {
|
||||
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;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
let comment_start = match get_parent_node(cx.tcx, item.hir_id()) {
|
||||
Node::Crate(parent_mod) => comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item),
|
||||
Node::Item(parent_item) => {
|
||||
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;
|
||||
},
|
||||
};
|
||||
|
||||
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
|
||||
}
|
||||
},
|
||||
Node::Stmt(stmt) => {
|
||||
if let Node::Block(block) = get_parent_node(cx.tcx, stmt.hir_id) {
|
||||
walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo)
|
||||
} 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,
|
||||
}
|
||||
};
|
||||
}
|
||||
// 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.
|
||||
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
|
||||
}
|
||||
@ -512,32 +504,30 @@ fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> H
|
||||
return HasSafetyComment::No;
|
||||
}
|
||||
|
||||
if let Some(parent_node) = 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),
|
||||
_ => return HasSafetyComment::Maybe,
|
||||
};
|
||||
let comment_start = match get_parent_node(cx.tcx, hir_id) {
|
||||
Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo),
|
||||
_ => 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(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,
|
||||
}
|
||||
};
|
||||
}
|
||||
let source_map = cx.sess().source_map();
|
||||
if let Some(comment_start) = comment_start
|
||||
&& let Ok(unsafe_line) = source_map.lookup_line(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
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ fn expr_needs_inferred_result<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -
|
||||
return false;
|
||||
}
|
||||
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)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -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>> {
|
||||
let map = cx.tcx.hir();
|
||||
|
||||
match cx.tcx.parent_hir_node(hir_id) {
|
||||
hir::Node::Local(local) => Some(local),
|
||||
hir::Node::Pat(pattern) => get_parent_local_hir_id(cx, pattern.hir_id),
|
||||
|
@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! - 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 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)
|
||||
}
|
||||
|
||||
#[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 {
|
||||
use ItemKind::*;
|
||||
match (l, r) {
|
||||
@ -690,7 +690,9 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
|
||||
match (&l.kind, &r.kind) {
|
||||
(Paren(l), _) => 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),
|
||||
(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),
|
||||
|
@ -40,7 +40,7 @@ fn docs_link(diag: &mut DiagnosticBuilder<'_, ()>, lint: &'static Lint) {
|
||||
///
|
||||
/// ```ignore
|
||||
/// 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);
|
||||
/// | ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -65,7 +65,7 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
|
||||
///
|
||||
/// ```text
|
||||
/// 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;
|
||||
/// | ^^^^^^^^^^^^
|
||||
@ -103,14 +103,14 @@ pub fn span_lint_and_help<T: LintContext>(
|
||||
///
|
||||
/// ```text
|
||||
/// 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);
|
||||
/// | ^^^^^^^^^^^^^^^^^^^
|
||||
/// |
|
||||
/// = note: `-D clippy::forget-ref` implied by `-D warnings`
|
||||
/// note: argument has type &SomeStruct
|
||||
/// --> $DIR/drop_forget_ref.rs:10:12
|
||||
/// --> tests/ui/drop_forget_ref.rs:10:12
|
||||
/// |
|
||||
/// 10 | forget(&SomeStruct);
|
||||
/// | ^^^^^^^^^^^
|
||||
@ -186,7 +186,7 @@ pub fn span_lint_hir_and_then(
|
||||
///
|
||||
/// ```text
|
||||
/// 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);
|
||||
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
|
||||
|
@ -132,7 +132,6 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
}
|
||||
|
||||
/// Checks whether two blocks are the same.
|
||||
#[expect(clippy::similar_names)]
|
||||
fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
|
||||
use TokenKind::{Semi, Whitespace};
|
||||
if left.stmts.len() != right.stmts.len() {
|
||||
@ -247,7 +246,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
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 {
|
||||
if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) {
|
||||
return false;
|
||||
@ -463,7 +462,6 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::similar_names)]
|
||||
fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
|
||||
match (left, right) {
|
||||
(&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()
|
||||
}
|
||||
|
||||
#[expect(clippy::similar_names)]
|
||||
fn eq_span_tokens(
|
||||
cx: &LateContext<'_>,
|
||||
left: impl SpanRange,
|
||||
|
@ -14,7 +14,7 @@
|
||||
clippy::missing_panics_doc,
|
||||
clippy::must_use_candidate,
|
||||
rustc::diagnostic_outside_of_impl,
|
||||
rustc::untranslatable_diagnostic,
|
||||
rustc::untranslatable_diagnostic
|
||||
)]
|
||||
// warn on the same lints as `clippy_lints`
|
||||
#![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.
|
||||
pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
|
||||
Some(tcx.parent_hir_node(id))
|
||||
pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Node<'_> {
|
||||
tcx.parent_hir_node(id)
|
||||
}
|
||||
|
||||
/// 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
|
||||
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) {
|
||||
Some(Node::Expr(parent)) => Some(parent),
|
||||
Node::Expr(parent) => Some(parent),
|
||||
_ => 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.
|
||||
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> {
|
||||
|
@ -11,7 +11,7 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
|
||||
["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
|
||||
["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 BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
|
||||
pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
|
||||
|
@ -174,9 +174,8 @@ fn check_rvalue<'tcx>(
|
||||
))
|
||||
}
|
||||
},
|
||||
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions, _) | Rvalue::ShallowInitBox(_, _) => {
|
||||
Ok(())
|
||||
},
|
||||
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions, _)
|
||||
| Rvalue::ShallowInitBox(_, _) => Ok(()),
|
||||
Rvalue::UnaryOp(_, operand) => {
|
||||
let ty = operand.ty(body, tcx);
|
||||
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 {
|
||||
// FIXME(effects, fee1-dead) revert to const destruct once it works again
|
||||
#[expect(unused)]
|
||||
|
@ -8,7 +8,7 @@ use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_session::Session;
|
||||
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::ops::Range;
|
||||
|
||||
|
@ -272,7 +272,9 @@ pub fn main() {
|
||||
},
|
||||
_ => 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(), "clippy".into()])
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
// We enable Clippy if one of the following conditions is met
|
||||
|
@ -4,6 +4,7 @@
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
#![allow(unused_extern_crates)]
|
||||
|
||||
use ui_test::spanned::Spanned;
|
||||
use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling};
|
||||
|
||||
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 mut config = Config {
|
||||
mode: Mode::Yolo {
|
||||
rustfix: ui_test::RustfixMode::Everything,
|
||||
},
|
||||
output_conflict_handling: OutputConflictHandling::Error,
|
||||
filter_files: env::var("TESTNAME")
|
||||
.map(|filters| filters.split(',').map(str::to_string).collect())
|
||||
.unwrap_or_default(),
|
||||
target: None,
|
||||
bless_command: Some("cargo uibless".into()),
|
||||
out_dir: target_dir.join("ui_test"),
|
||||
..Config::rustc(Path::new("tests").join(test_dir))
|
||||
};
|
||||
config.with_args(&args, /* bless by default */ false);
|
||||
if let OutputConflictHandling::Error(err) = &mut config.output_conflict_handling {
|
||||
*err = "cargo uibless".into();
|
||||
}
|
||||
config.comment_defaults.base().mode = Some(Spanned::dummy(Mode::Yolo {
|
||||
rustfix: ui_test::RustfixMode::Everything,
|
||||
}))
|
||||
.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 deps_path = current_exe_path.parent().unwrap();
|
||||
let profile_path = deps_path.parent().unwrap();
|
||||
@ -179,9 +181,7 @@ fn run_internal_tests() {
|
||||
return;
|
||||
}
|
||||
let (mut config, args) = base_config("ui-internal");
|
||||
if let OutputConflictHandling::Error(err) = &mut config.output_conflict_handling {
|
||||
*err = "cargo uitest --features internal -- -- --bless".into();
|
||||
}
|
||||
config.bless_command = Some("cargo uitest --features internal -- -- --bless".into());
|
||||
|
||||
ui_test::run_tests_generic(
|
||||
vec![config],
|
||||
@ -196,8 +196,10 @@ fn run_ui_toml() {
|
||||
let (mut config, args) = base_config("ui-toml");
|
||||
|
||||
config
|
||||
.stderr_filters
|
||||
.push((Match::from(env::current_dir().unwrap().as_path()), b"$DIR"));
|
||||
.comment_defaults
|
||||
.base()
|
||||
.normalize_stderr
|
||||
.push((Match::from(env::current_dir().unwrap().as_path()), b"$DIR".into()));
|
||||
|
||||
ui_test::run_tests_generic(
|
||||
vec![config],
|
||||
@ -213,6 +215,8 @@ fn run_ui_toml() {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Allow `Default::default` as `OptWithSpan` is not nameable
|
||||
#[allow(clippy::default_trait_access)]
|
||||
fn run_ui_cargo() {
|
||||
if IS_RUSTC_TEST_SUITE {
|
||||
return;
|
||||
@ -234,11 +238,13 @@ fn run_ui_cargo() {
|
||||
} else {
|
||||
"cargo-clippy"
|
||||
});
|
||||
config.edition = None;
|
||||
config.comment_defaults.base().edition = Default::default();
|
||||
|
||||
config
|
||||
.stderr_filters
|
||||
.push((Match::from(env::current_dir().unwrap().as_path()), b"$DIR"));
|
||||
.comment_defaults
|
||||
.base()
|
||||
.normalize_stderr
|
||||
.push((Match::from(env::current_dir().unwrap().as_path()), b"$DIR".into()));
|
||||
|
||||
let ignored_32bit = |path: &Path| {
|
||||
// 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(
|
||||
vec![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| {},
|
||||
status_emitter::Text::from(args.format),
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 | | #[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`
|
||||
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)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
@ -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)
|
||||
|
||||
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 | | #[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)
|
||||
|
||||
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 | | #[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)
|
||||
|
||||
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 | | pub clippy::MISSING_ATTRIBUTE_TWO,
|
||||
|
@ -1,5 +1,5 @@
|
||||
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
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -9,7 +9,7 @@ LL | /// Check for lint formulations that are correct
|
||||
= help: to override `-D warnings` add `#[allow(clippy::almost_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
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 | | 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)`
|
||||
|
|
||||
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)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
= note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
|
||||
|
||||
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 | | 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)`
|
||||
|
||||
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 | | 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)`
|
||||
|
||||
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 | | 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)`
|
||||
|
||||
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 | | db.note(note_msg);
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 | | /// ### What it does
|
||||
@ -11,7 +11,7 @@ LL | | }
|
||||
| |_^
|
||||
|
|
||||
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)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 | | pub clippy::TEST_LINT_DEFAULT,
|
||||
@ -10,7 +10,7 @@ LL | | }
|
||||
| |_^
|
||||
|
|
||||
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)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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, |_| {});
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -8,7 +8,7 @@ LL | cx.span_lint(lint, span, msg, |_| {});
|
||||
= help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]`
|
||||
|
||||
error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint`
|
||||
--> $DIR/disallow_span_lint.rs:24:5
|
||||
--> tests/ui-internal/disallow_span_lint.rs:24:5
|
||||
|
|
||||
LL | tcx.node_span_lint(lint, hir_id, span, msg, |_| {});
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -1,30 +1,30 @@
|
||||
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");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32`
|
||||
|
|
||||
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)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
= note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]`
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^ help: try: `rustc_span::sym::f32`
|
||||
|
||||
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");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro`
|
||||
|
||||
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");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::kw::SelfLower`
|
||||
|
@ -1,11 +1,11 @@
|
||||
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 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
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)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
@ -17,7 +17,7 @@ LL + extract_msrv_attr!(LateContext);
|
||||
|
|
||||
|
||||
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 {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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"];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -8,13 +8,13 @@ LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"
|
||||
= help: to override `-D warnings` add `#[allow(clippy::invalid_paths)]`
|
||||
|
||||
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"];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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"];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 | | pub clippy::TEST_LINT,
|
||||
@ -10,7 +10,7 @@ LL | | }
|
||||
| |_^
|
||||
|
|
||||
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)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
@ -1,11 +1,11 @@
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()`
|
||||
|
|
||||
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)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
@ -1,72 +1,72 @@
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
|
||||
|
|
||||
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)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
= note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]`
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
|
||||
|
||||
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"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)`
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
|
||||
|
||||
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"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)`
|
||||
|
||||
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"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)`
|
||||
|
||||
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"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)`
|
||||
|
||||
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"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)`
|
||||
|
||||
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"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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
|
||||
|
||||
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"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)`
|
||||
|
||||
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"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)`
|
||||
|
||||
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"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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`
|
||||
--> $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"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)`
|
||||
|
@ -1,5 +1,5 @@
|
||||
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"];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -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)]`
|
||||
|
||||
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"];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -17,10 +17,10 @@ LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]
|
||||
= help: convert all references to use `LangItem::DerefMut`
|
||||
|
||||
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`
|
||||
|
||||
|
@ -1,36 +1,36 @@
|
||||
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";
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy`
|
||||
|
|
||||
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)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
= note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]`
|
||||
|
||||
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";
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower`
|
||||
|
||||
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";
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper`
|
||||
|
||||
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";
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy`
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name`
|
||||
|
@ -1,5 +1,5 @@
|
||||
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;
|
||||
| ^^^^^^^^^^^^^
|
||||
@ -8,19 +8,19 @@ LL | std::f32::MAX;
|
||||
= help: to override `-D warnings` add `#[allow(clippy::absolute_paths)]`
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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;
|
||||
| ^^^^^^^^^^^^^
|
||||
@ -8,61 +8,61 @@ LL | std::f32::MAX;
|
||||
= help: to override `-D warnings` add `#[allow(clippy::absolute_paths)]`
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -13,7 +13,7 @@ LL + println!("val='{local_i32}'");
|
||||
|
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -25,7 +25,7 @@ LL + println!("Hello {} is {local_f64:.local_i32$}", "x");
|
||||
|
|
||||
|
||||
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);
|
||||
| ^^^
|
||||
@ -39,7 +39,7 @@ LL + println!("Hello x is {:.*}", local_i32, local_f64);
|
||||
|
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -51,7 +51,7 @@ LL + println!("Hello {local_i32} is {local_f64:.*}", 5);
|
||||
|
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -63,7 +63,7 @@ LL + println!("Hello {local_i32} is {local_f64:.*}", 5);
|
||||
|
|
||||
|
||||
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());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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;
|
||||
| ^^^^^^^^^
|
||||
@ -8,49 +8,49 @@ LL | let _ = Baz + Baz;
|
||||
= help: to override `-D warnings` add `#[allow(clippy::arithmetic_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;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^
|
||||
|
||||
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;
|
||||
| ^^^^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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];
|
||||
| -----^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -10,7 +10,7 @@ LL | const ABOVE: [u8; 11] = [0; 11];
|
||||
= help: to override `-D warnings` add `#[allow(clippy::large_const_arrays)]`
|
||||
|
||||
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];
|
||||
| ^^^^^^^
|
||||
@ -20,7 +20,7 @@ LL | const ABOVE: [u8; 11] = [0; 11];
|
||||
= help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]`
|
||||
|
||||
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];
|
||||
| ^^^^^^^^^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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");
|
||||
| ^^
|
||||
@ -9,13 +9,13 @@ LL | let _x = String::from("hello");
|
||||
= 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`
|
||||
--> $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);
|
||||
| ^
|
||||
|
||||
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!");
|
||||
| ^^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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) {
|
||||
| ^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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
|
||||
| ^^
|
||||
|
@ -1,17 +1,17 @@
|
||||
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
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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" ]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
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() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -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
|
||||
--> $DIR/dbg_macro.rs:9:8
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:9:8
|
||||
|
|
||||
LL | if dbg!(n <= 1) {
|
||||
| ^^^^^^^^^^^^
|
||||
@ -23,7 +23,7 @@ LL | if n <= 1 {
|
||||
| ~~~~~~
|
||||
|
||||
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)
|
||||
| ^^^^^^^
|
||||
@ -34,7 +34,7 @@ LL | 1
|
||||
|
|
||||
|
||||
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))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -45,7 +45,7 @@ LL | n * factorial(n - 1)
|
||||
|
|
||||
|
||||
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);
|
||||
| ^^^^^^^^
|
||||
@ -56,7 +56,7 @@ LL | 42;
|
||||
| ~~
|
||||
|
||||
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)));
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
@ -67,7 +67,7 @@ LL | dbg!(dbg!(42));
|
||||
| ~~~~~~~~~~~~~~
|
||||
|
||||
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));
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
@ -78,7 +78,7 @@ LL | foo(3) + factorial(4);
|
||||
| ~~~~~~~~~~~~
|
||||
|
||||
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));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -89,7 +89,7 @@ LL | (1, 2, dbg!(3, 4));
|
||||
| ~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -1,5 +1,5 @@
|
||||
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;
|
||||
| ^^^^^^^^ help: consider: `0x00FF_FFFF`
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user