mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-19 11:12:43 +00:00
Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
81fe8dc084
8
.github/workflows/remark.yml
vendored
8
.github/workflows/remark.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '14.x'
|
node-version: '18.x'
|
||||||
|
|
||||||
- name: Install remark
|
- name: Install remark
|
||||||
run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm
|
run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm
|
||||||
@ -29,19 +29,19 @@ jobs:
|
|||||||
- name: Install mdbook
|
- name: Install mdbook
|
||||||
run: |
|
run: |
|
||||||
mkdir mdbook
|
mkdir mdbook
|
||||||
curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.28/mdbook-v0.4.28-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
|
curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.34/mdbook-v0.4.34-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
|
||||||
echo `pwd`/mdbook >> $GITHUB_PATH
|
echo `pwd`/mdbook >> $GITHUB_PATH
|
||||||
|
|
||||||
# Run
|
# Run
|
||||||
- name: Check *.md files
|
- name: Check *.md files
|
||||||
run: git ls-files -z '*.md' | xargs -0 -n 1 -I {} ./node_modules/.bin/remark {} -u lint -f > /dev/null
|
run: ./node_modules/.bin/remark -u lint -f .
|
||||||
|
|
||||||
- name: Linkcheck book
|
- name: Linkcheck book
|
||||||
run: |
|
run: |
|
||||||
rustup toolchain install nightly --component rust-docs
|
rustup toolchain install nightly --component rust-docs
|
||||||
curl https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh -o linkcheck.sh
|
curl https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh -o linkcheck.sh
|
||||||
sh linkcheck.sh clippy --path ./book
|
sh linkcheck.sh clippy --path ./book
|
||||||
|
|
||||||
- name: Build mdbook
|
- name: Build mdbook
|
||||||
run: mdbook build book
|
run: mdbook build book
|
||||||
|
|
||||||
|
@ -5171,6 +5171,7 @@ Released 2018-09-13
|
|||||||
[`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign
|
[`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign
|
||||||
[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
|
[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
|
||||||
[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
|
[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
|
||||||
|
[`needless_borrows_for_generic_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args
|
||||||
[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
|
[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
|
||||||
[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
|
[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
|
||||||
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
|
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
|
||||||
@ -5245,6 +5246,7 @@ Released 2018-09-13
|
|||||||
[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
|
[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
|
||||||
[`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
|
[`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
|
||||||
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
|
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
|
||||||
|
[`path_ends_with_ext`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext
|
||||||
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
|
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
|
||||||
[`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false
|
[`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false
|
||||||
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
|
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
|
||||||
@ -5279,6 +5281,7 @@ Released 2018-09-13
|
|||||||
[`readonly_write_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#readonly_write_lock
|
[`readonly_write_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#readonly_write_lock
|
||||||
[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
|
[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
|
||||||
[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
|
[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
|
||||||
|
[`redundant_as_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_as_str
|
||||||
[`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block
|
[`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block
|
||||||
[`redundant_at_rest_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_at_rest_pattern
|
[`redundant_at_rest_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_at_rest_pattern
|
||||||
[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
|
[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
|
||||||
@ -5437,6 +5440,7 @@ Released 2018-09-13
|
|||||||
[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
|
[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
|
||||||
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
|
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
|
||||||
[`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
|
[`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
|
||||||
|
[`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor
|
||||||
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
|
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
|
||||||
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
|
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
|
||||||
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
|
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
|
||||||
@ -5574,5 +5578,6 @@ Released 2018-09-13
|
|||||||
[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
|
[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
|
||||||
[`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments
|
[`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments
|
||||||
[`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates
|
[`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates
|
||||||
|
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
|
||||||
[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow
|
[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow
|
||||||
<!-- end autogenerated links to configuration documentation -->
|
<!-- end autogenerated links to configuration documentation -->
|
||||||
|
@ -38,7 +38,6 @@ itertools = "0.10.1"
|
|||||||
|
|
||||||
# UI test dependencies
|
# UI test dependencies
|
||||||
clippy_utils = { path = "clippy_utils" }
|
clippy_utils = { path = "clippy_utils" }
|
||||||
derive-new = "0.5"
|
|
||||||
if_chain = "1.0"
|
if_chain = "1.0"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
serde = { version = "1.0.125", features = ["derive"] }
|
serde = { version = "1.0.125", features = ["derive"] }
|
||||||
|
@ -703,7 +703,7 @@ Minimum chars an ident can have, anything below or equal to this will be linted.
|
|||||||
## `accept-comment-above-statement`
|
## `accept-comment-above-statement`
|
||||||
Whether to accept a safety comment to be placed above the statement containing the `unsafe` block
|
Whether to accept a safety comment to be placed above the statement containing the `unsafe` block
|
||||||
|
|
||||||
**Default Value:** `false` (`bool`)
|
**Default Value:** `true` (`bool`)
|
||||||
|
|
||||||
---
|
---
|
||||||
**Affected lints:**
|
**Affected lints:**
|
||||||
@ -713,7 +713,7 @@ Whether to accept a safety comment to be placed above the statement containing t
|
|||||||
## `accept-comment-above-attributes`
|
## `accept-comment-above-attributes`
|
||||||
Whether to accept a safety comment to be placed above the attributes for the `unsafe` block
|
Whether to accept a safety comment to be placed above the attributes for the `unsafe` block
|
||||||
|
|
||||||
**Default Value:** `false` (`bool`)
|
**Default Value:** `true` (`bool`)
|
||||||
|
|
||||||
---
|
---
|
||||||
**Affected lints:**
|
**Affected lints:**
|
||||||
@ -751,6 +751,16 @@ Which crates to allow absolute paths from
|
|||||||
* [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths)
|
* [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths)
|
||||||
|
|
||||||
|
|
||||||
|
## `allowed-dotfiles`
|
||||||
|
Additional dotfiles (files or directories starting with a dot) to allow
|
||||||
|
|
||||||
|
**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet<String>`)
|
||||||
|
|
||||||
|
---
|
||||||
|
**Affected lints:**
|
||||||
|
* [`path_ends_with_ext`](https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext)
|
||||||
|
|
||||||
|
|
||||||
## `enforce-iter-loop-reborrow`
|
## `enforce-iter-loop-reborrow`
|
||||||
#### Example
|
#### Example
|
||||||
```
|
```
|
||||||
|
@ -616,7 +616,7 @@ fn check_should_panic_reason(cx: &LateContext<'_>, attr: &Attribute) {
|
|||||||
attr.span,
|
attr.span,
|
||||||
"#[should_panic] attribute without a reason",
|
"#[should_panic] attribute without a reason",
|
||||||
"consider specifying the expected panic",
|
"consider specifying the expected panic",
|
||||||
r#"#[should_panic(expected = /* panic message */)]"#.into(),
|
"#[should_panic(expected = /* panic message */)]".into(),
|
||||||
Applicability::HasPlaceholders,
|
Applicability::HasPlaceholders,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ pub(super) fn check(
|
|||||||
// The suggestion is to use a function call, so if the original expression
|
// The suggestion is to use a function call, so if the original expression
|
||||||
// has parens on the outside, they are no longer needed.
|
// has parens on the outside, they are no longer needed.
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let opt = snippet_opt(cx, cast_op.span);
|
let opt = snippet_opt(cx, cast_op.span.source_callsite());
|
||||||
let sugg = opt.as_ref().map_or_else(
|
let sugg = opt.as_ref().map_or_else(
|
||||||
|| {
|
|| {
|
||||||
applicability = Applicability::HasPlaceholders;
|
applicability = Applicability::HasPlaceholders;
|
||||||
|
@ -44,7 +44,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
|
|||||||
.unwrap_or(u64::max_value())
|
.unwrap_or(u64::max_value())
|
||||||
.min(apply_reductions(cx, nbits, left, signed)),
|
.min(apply_reductions(cx, nbits, left, signed)),
|
||||||
BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
|
BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
|
||||||
.saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))),
|
.saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).unwrap_or_default())),
|
||||||
_ => nbits,
|
_ => nbits,
|
||||||
},
|
},
|
||||||
ExprKind::MethodCall(method, left, [right], _) => {
|
ExprKind::MethodCall(method, left, [right], _) => {
|
||||||
|
@ -20,6 +20,7 @@ mod ptr_as_ptr;
|
|||||||
mod ptr_cast_constness;
|
mod ptr_cast_constness;
|
||||||
mod unnecessary_cast;
|
mod unnecessary_cast;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
mod zero_ptr;
|
||||||
|
|
||||||
use clippy_utils::is_hir_ty_cfg_dependant;
|
use clippy_utils::is_hir_ty_cfg_dependant;
|
||||||
use clippy_utils::msrvs::{self, Msrv};
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
@ -665,6 +666,29 @@ declare_clippy_lint! {
|
|||||||
"casting a known floating-point NaN into an integer"
|
"casting a known floating-point NaN into an integer"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Catch casts from `0` to some pointer type
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// This generally means `null` and is better expressed as
|
||||||
|
/// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// let a = 0 as *const u32;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// let a = std::ptr::null::<u32>();
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "pre 1.29.0"]
|
||||||
|
pub ZERO_PTR,
|
||||||
|
style,
|
||||||
|
"using `0 as *{const, mut} T`"
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Casts {
|
pub struct Casts {
|
||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
@ -699,6 +723,7 @@ impl_lint_pass!(Casts => [
|
|||||||
CAST_SLICE_FROM_RAW_PARTS,
|
CAST_SLICE_FROM_RAW_PARTS,
|
||||||
AS_PTR_CAST_MUT,
|
AS_PTR_CAST_MUT,
|
||||||
CAST_NAN_TO_INT,
|
CAST_NAN_TO_INT,
|
||||||
|
ZERO_PTR,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||||
@ -729,6 +754,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||||||
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
|
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||||
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
|
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||||
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
|
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||||
|
zero_ptr::check(cx, expr, cast_expr, cast_to_hir);
|
||||||
|
|
||||||
if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
|
if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
|
||||||
cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span);
|
cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span);
|
||||||
|
39
clippy_lints/src/casts/zero_ptr.rs
Normal file
39
clippy_lints/src/casts/zero_ptr.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::source::snippet_opt;
|
||||||
|
use clippy_utils::{in_constant, is_integer_literal, std_or_core};
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::{Expr, Mutability, Ty, TyKind};
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
|
||||||
|
use super::ZERO_PTR;
|
||||||
|
|
||||||
|
pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) {
|
||||||
|
if let TyKind::Ptr(ref mut_ty) = to.kind
|
||||||
|
&& is_integer_literal(from, 0)
|
||||||
|
&& !in_constant(cx, from.hir_id)
|
||||||
|
&& let Some(std_or_core) = std_or_core(cx)
|
||||||
|
{
|
||||||
|
let (msg, sugg_fn) = match mut_ty.mutbl {
|
||||||
|
Mutability::Mut => ("`0 as *mut _` detected", "ptr::null_mut"),
|
||||||
|
Mutability::Not => ("`0 as *const _` detected", "ptr::null"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let sugg = if let TyKind::Infer = mut_ty.ty.kind {
|
||||||
|
format!("{std_or_core}::{sugg_fn}()")
|
||||||
|
} else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) {
|
||||||
|
format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
ZERO_PTR,
|
||||||
|
expr.span,
|
||||||
|
msg,
|
||||||
|
"try",
|
||||||
|
sugg,
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -97,6 +97,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||||||
crate::casts::PTR_AS_PTR_INFO,
|
crate::casts::PTR_AS_PTR_INFO,
|
||||||
crate::casts::PTR_CAST_CONSTNESS_INFO,
|
crate::casts::PTR_CAST_CONSTNESS_INFO,
|
||||||
crate::casts::UNNECESSARY_CAST_INFO,
|
crate::casts::UNNECESSARY_CAST_INFO,
|
||||||
|
crate::casts::ZERO_PTR_INFO,
|
||||||
crate::checked_conversions::CHECKED_CONVERSIONS_INFO,
|
crate::checked_conversions::CHECKED_CONVERSIONS_INFO,
|
||||||
crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
|
crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
|
||||||
crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
|
crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
|
||||||
@ -399,9 +400,11 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||||||
crate::methods::OR_FUN_CALL_INFO,
|
crate::methods::OR_FUN_CALL_INFO,
|
||||||
crate::methods::OR_THEN_UNWRAP_INFO,
|
crate::methods::OR_THEN_UNWRAP_INFO,
|
||||||
crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO,
|
crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO,
|
||||||
|
crate::methods::PATH_ENDS_WITH_EXT_INFO,
|
||||||
crate::methods::RANGE_ZIP_WITH_LEN_INFO,
|
crate::methods::RANGE_ZIP_WITH_LEN_INFO,
|
||||||
crate::methods::READONLY_WRITE_LOCK_INFO,
|
crate::methods::READONLY_WRITE_LOCK_INFO,
|
||||||
crate::methods::READ_LINE_WITHOUT_TRIM_INFO,
|
crate::methods::READ_LINE_WITHOUT_TRIM_INFO,
|
||||||
|
crate::methods::REDUNDANT_AS_STR_INFO,
|
||||||
crate::methods::REPEAT_ONCE_INFO,
|
crate::methods::REPEAT_ONCE_INFO,
|
||||||
crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO,
|
crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO,
|
||||||
crate::methods::SEARCH_IS_SOME_INFO,
|
crate::methods::SEARCH_IS_SOME_INFO,
|
||||||
@ -441,7 +444,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||||||
crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,
|
crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,
|
||||||
crate::misc::TOPLEVEL_REF_ARG_INFO,
|
crate::misc::TOPLEVEL_REF_ARG_INFO,
|
||||||
crate::misc::USED_UNDERSCORE_BINDING_INFO,
|
crate::misc::USED_UNDERSCORE_BINDING_INFO,
|
||||||
crate::misc::ZERO_PTR_INFO,
|
|
||||||
crate::misc_early::BUILTIN_TYPE_SHADOW_INFO,
|
crate::misc_early::BUILTIN_TYPE_SHADOW_INFO,
|
||||||
crate::misc_early::DOUBLE_NEG_INFO,
|
crate::misc_early::DOUBLE_NEG_INFO,
|
||||||
crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO,
|
crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO,
|
||||||
@ -479,6 +481,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||||||
crate::needless_bool::NEEDLESS_BOOL_INFO,
|
crate::needless_bool::NEEDLESS_BOOL_INFO,
|
||||||
crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
|
crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
|
||||||
crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
|
crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
|
||||||
|
crate::needless_borrows_for_generic_args::NEEDLESS_BORROWS_FOR_GENERIC_ARGS_INFO,
|
||||||
crate::needless_continue::NEEDLESS_CONTINUE_INFO,
|
crate::needless_continue::NEEDLESS_CONTINUE_INFO,
|
||||||
crate::needless_else::NEEDLESS_ELSE_INFO,
|
crate::needless_else::NEEDLESS_ELSE_INFO,
|
||||||
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
|
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
|
||||||
@ -671,6 +674,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||||||
crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO,
|
crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO,
|
||||||
crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO,
|
crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO,
|
||||||
crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO,
|
crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO,
|
||||||
|
crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO,
|
||||||
crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
|
crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
|
||||||
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
|
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
|
||||||
crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,
|
crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use clippy_utils::diagnostics::span_lint_and_help;
|
use clippy_utils::diagnostics::span_lint_and_help;
|
||||||
use rustc_hir::{self as hir, HirId, Item, ItemKind};
|
use rustc_hir::{HirId, Item, ItemKind};
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::layout::LayoutOf;
|
use rustc_middle::ty::layout::LayoutOf;
|
||||||
|
use rustc_middle::ty::{self, FieldDef, GenericArg, List};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
|
|
||||||
@ -52,7 +52,10 @@ declare_lint_pass!(DefaultUnionRepresentation => [DEFAULT_UNION_REPRESENTATION])
|
|||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation {
|
impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation {
|
||||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||||
if is_union_with_two_non_zst_fields(cx, item) && !has_c_repr_attr(cx, item.hir_id()) {
|
if !item.span.from_expansion()
|
||||||
|
&& is_union_with_two_non_zst_fields(cx, item)
|
||||||
|
&& !has_c_repr_attr(cx, item.hir_id())
|
||||||
|
{
|
||||||
span_lint_and_help(
|
span_lint_and_help(
|
||||||
cx,
|
cx,
|
||||||
DEFAULT_UNION_REPRESENTATION,
|
DEFAULT_UNION_REPRESENTATION,
|
||||||
@ -73,18 +76,17 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation {
|
|||||||
/// if there is only one field left after ignoring ZST fields then the offset
|
/// if there is only one field left after ignoring ZST fields then the offset
|
||||||
/// of that field does not matter either.)
|
/// of that field does not matter either.)
|
||||||
fn is_union_with_two_non_zst_fields(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
|
fn is_union_with_two_non_zst_fields(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
|
||||||
if let ItemKind::Union(data, _) = &item.kind {
|
if let ItemKind::Union(..) = &item.kind
|
||||||
data.fields().iter().filter(|f| !is_zst(cx, f.ty)).count() >= 2
|
&& let ty::Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind()
|
||||||
|
{
|
||||||
|
adt_def.all_fields().filter(|f| !is_zst(cx, f, args)).count() >= 2
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_zst(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) -> bool {
|
fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: &'tcx List<GenericArg<'tcx>>) -> bool {
|
||||||
if hir_ty.span.from_expansion() {
|
let ty = field.ty(cx.tcx, args);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
|
||||||
if let Ok(layout) = cx.layout_of(ty) {
|
if let Ok(layout) = cx.layout_of(ty) {
|
||||||
layout.is_zst()
|
layout.is_zst()
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,41 +1,24 @@
|
|||||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
||||||
use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
|
|
||||||
use clippy_utils::msrvs::{self, Msrv};
|
|
||||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||||
use clippy_utils::sugg::has_enclosing_paren;
|
use clippy_utils::sugg::has_enclosing_paren;
|
||||||
use clippy_utils::ty::{implements_trait, is_copy, peel_mid_ty_refs};
|
use clippy_utils::ty::{implements_trait, peel_mid_ty_refs};
|
||||||
use clippy_utils::{
|
use clippy_utils::{
|
||||||
expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
|
expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use hir::def::DefKind;
|
|
||||||
use hir::MatchSource;
|
|
||||||
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
|
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
|
||||||
use rustc_data_structures::fx::FxIndexMap;
|
use rustc_data_structures::fx::FxIndexMap;
|
||||||
use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch};
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::Res;
|
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
|
||||||
use rustc_hir::intravisit::{walk_ty, Visitor};
|
use rustc_hir::intravisit::{walk_ty, Visitor};
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind,
|
self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
|
||||||
Path, QPath, TyKind, UnOp,
|
Pat, PatKind, Path, QPath, TyKind, UnOp,
|
||||||
};
|
};
|
||||||
use rustc_index::bit_set::BitSet;
|
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::mir::{Rvalue, StatementKind};
|
|
||||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt, TypeckResults};
|
||||||
self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamEnv, ParamTy, ProjectionPredicate, Ty,
|
|
||||||
TyCtxt, TypeVisitableExt, TypeckResults,
|
|
||||||
};
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::{Span, Symbol};
|
use rustc_span::{Span, Symbol};
|
||||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
|
||||||
use rustc_trait_selection::traits::{Obligation, ObligationCause};
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
@ -183,24 +166,6 @@ pub struct Dereferencing<'tcx> {
|
|||||||
///
|
///
|
||||||
/// e.g. `m!(x) | Foo::Bar(ref x)`
|
/// e.g. `m!(x) | Foo::Bar(ref x)`
|
||||||
ref_locals: FxIndexMap<HirId, Option<RefPat>>,
|
ref_locals: FxIndexMap<HirId, Option<RefPat>>,
|
||||||
|
|
||||||
/// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by
|
|
||||||
/// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead
|
|
||||||
/// be moved.
|
|
||||||
possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
|
||||||
|
|
||||||
// `IntoIterator` for arrays requires Rust 1.53.
|
|
||||||
msrv: Msrv,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> Dereferencing<'tcx> {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(msrv: Msrv) -> Self {
|
|
||||||
Self {
|
|
||||||
msrv,
|
|
||||||
..Dereferencing::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -355,52 +320,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||||||
));
|
));
|
||||||
},
|
},
|
||||||
(Some(use_cx), RefOp::AddrOf(mutability)) => {
|
(Some(use_cx), RefOp::AddrOf(mutability)) => {
|
||||||
let defined_ty = use_cx.node.defined_ty(cx);
|
|
||||||
|
|
||||||
// Check needless_borrow for generic arguments.
|
|
||||||
if !use_cx.is_ty_unified
|
|
||||||
&& let Some(DefinedTy::Mir(ty)) = defined_ty
|
|
||||||
&& let ty::Param(ty) = *ty.value.skip_binder().kind()
|
|
||||||
&& let Some((hir_id, fn_id, i)) = match use_cx.node {
|
|
||||||
ExprUseNode::MethodArg(_, _, 0) => None,
|
|
||||||
ExprUseNode::MethodArg(hir_id, None, i) => {
|
|
||||||
typeck.type_dependent_def_id(hir_id).map(|id| (hir_id, id, i))
|
|
||||||
},
|
|
||||||
ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i)
|
|
||||||
if !path_has_args(p) => match typeck.qpath_res(p, hir_id) {
|
|
||||||
Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => {
|
|
||||||
Some((hir_id, id, i))
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
} && let count = needless_borrow_generic_arg_count(
|
|
||||||
cx,
|
|
||||||
&mut self.possible_borrowers,
|
|
||||||
fn_id,
|
|
||||||
typeck.node_args(hir_id),
|
|
||||||
i,
|
|
||||||
ty,
|
|
||||||
expr,
|
|
||||||
&self.msrv,
|
|
||||||
) && count != 0
|
|
||||||
{
|
|
||||||
self.state = Some((
|
|
||||||
State::DerefedBorrow(DerefedBorrow {
|
|
||||||
count: count - 1,
|
|
||||||
msg: "the borrowed expression implements the required traits",
|
|
||||||
stability: TyCoercionStability::None,
|
|
||||||
for_field_access: None,
|
|
||||||
}),
|
|
||||||
StateData {
|
|
||||||
span: expr.span,
|
|
||||||
hir_id: expr.hir_id,
|
|
||||||
adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target),
|
|
||||||
},
|
|
||||||
));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the number of times the borrow is auto-derefed.
|
// Find the number of times the borrow is auto-derefed.
|
||||||
let mut iter = use_cx.adjustments.iter();
|
let mut iter = use_cx.adjustments.iter();
|
||||||
let mut deref_count = 0usize;
|
let mut deref_count = 0usize;
|
||||||
@ -419,7 +338,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let stability = defined_ty.map_or(TyCoercionStability::None, |ty| {
|
let stability = use_cx.node.defined_ty(cx).map_or(TyCoercionStability::None, |ty| {
|
||||||
TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return())
|
TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return())
|
||||||
});
|
});
|
||||||
let can_auto_borrow = match use_cx.node {
|
let can_auto_borrow = match use_cx.node {
|
||||||
@ -700,12 +619,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
|
fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
|
||||||
if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| {
|
|
||||||
local_def_id == cx.tcx.hir().body_owner_def_id(body.id())
|
|
||||||
}) {
|
|
||||||
self.possible_borrowers.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if Some(body.id()) == self.current_body {
|
if Some(body.id()) == self.current_body {
|
||||||
for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
|
for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
|
||||||
let replacements = pat.replacements;
|
let replacements = pat.replacements;
|
||||||
@ -729,8 +642,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||||||
self.current_body = None;
|
self.current_body = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extract_msrv_attr!(LateContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_parse_ref_op<'tcx>(
|
fn try_parse_ref_op<'tcx>(
|
||||||
@ -788,13 +699,6 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_has_args(p: &QPath<'_>) -> bool {
|
|
||||||
match *p {
|
|
||||||
QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
|
fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
|
||||||
if let Some(parent) = get_parent_expr(cx, e)
|
if let Some(parent) = get_parent_expr(cx, e)
|
||||||
&& parent.span.ctxt() == e.span.ctxt()
|
&& parent.span.ctxt() == e.span.ctxt()
|
||||||
@ -980,274 +884,6 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
|
|||||||
v.0
|
v.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks for the number of borrow expressions which can be removed from the given expression
|
|
||||||
/// where the expression is used as an argument to a function expecting a generic type.
|
|
||||||
///
|
|
||||||
/// The following constraints will be checked:
|
|
||||||
/// * The borrowed expression meets all the generic type's constraints.
|
|
||||||
/// * The generic type appears only once in the functions signature.
|
|
||||||
/// * The borrowed value will not be moved if it is used later in the function.
|
|
||||||
#[expect(clippy::too_many_arguments)]
|
|
||||||
fn needless_borrow_generic_arg_count<'tcx>(
|
|
||||||
cx: &LateContext<'tcx>,
|
|
||||||
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
|
||||||
fn_id: DefId,
|
|
||||||
callee_args: &'tcx List<GenericArg<'tcx>>,
|
|
||||||
arg_index: usize,
|
|
||||||
param_ty: ParamTy,
|
|
||||||
mut expr: &Expr<'tcx>,
|
|
||||||
msrv: &Msrv,
|
|
||||||
) -> usize {
|
|
||||||
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
|
|
||||||
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
|
|
||||||
|
|
||||||
let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder();
|
|
||||||
let predicates = cx.tcx.param_env(fn_id).caller_bounds();
|
|
||||||
let projection_predicates = predicates
|
|
||||||
.iter()
|
|
||||||
.filter_map(|predicate| {
|
|
||||||
if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
|
|
||||||
Some(projection_predicate)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut trait_with_ref_mut_self_method = false;
|
|
||||||
|
|
||||||
// If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
|
|
||||||
if predicates
|
|
||||||
.iter()
|
|
||||||
.filter_map(|predicate| {
|
|
||||||
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
|
||||||
&& trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
|
|
||||||
{
|
|
||||||
Some(trait_predicate.trait_ref.def_id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.inspect(|trait_def_id| {
|
|
||||||
trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
|
|
||||||
})
|
|
||||||
.all(|trait_def_id| {
|
|
||||||
Some(trait_def_id) == destruct_trait_def_id
|
|
||||||
|| Some(trait_def_id) == sized_trait_def_id
|
|
||||||
|| cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
|
|
||||||
})
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See:
|
|
||||||
// - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201
|
|
||||||
// - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232
|
|
||||||
if projection_predicates
|
|
||||||
.iter()
|
|
||||||
.any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// `args_with_referent_ty` can be constructed outside of `check_referent` because the same
|
|
||||||
// elements are modified each time `check_referent` is called.
|
|
||||||
let mut args_with_referent_ty = callee_args.to_vec();
|
|
||||||
|
|
||||||
let mut check_reference_and_referent = |reference, referent| {
|
|
||||||
let referent_ty = cx.typeck_results().expr_ty(referent);
|
|
||||||
|
|
||||||
if !is_copy(cx, referent_ty)
|
|
||||||
&& (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
|
|
||||||
|| !referent_used_exactly_once(cx, possible_borrowers, reference))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
|
|
||||||
if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !replace_types(
|
|
||||||
cx,
|
|
||||||
param_ty,
|
|
||||||
referent_ty,
|
|
||||||
fn_sig,
|
|
||||||
arg_index,
|
|
||||||
&projection_predicates,
|
|
||||||
&mut args_with_referent_ty,
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
predicates.iter().all(|predicate| {
|
|
||||||
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
|
||||||
&& cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
|
|
||||||
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
|
|
||||||
&& let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack()
|
|
||||||
&& ty.is_array()
|
|
||||||
&& !msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty);
|
|
||||||
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
|
|
||||||
let infcx = cx.tcx.infer_ctxt().build();
|
|
||||||
infcx.predicate_must_hold_modulo_regions(&obligation)
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut count = 0;
|
|
||||||
while let ExprKind::AddrOf(_, _, referent) = expr.kind {
|
|
||||||
if !check_reference_and_referent(expr, referent) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
expr = referent;
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
count
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
|
|
||||||
cx.tcx
|
|
||||||
.associated_items(trait_def_id)
|
|
||||||
.in_definition_order()
|
|
||||||
.any(|assoc_item| {
|
|
||||||
if assoc_item.fn_has_self_parameter {
|
|
||||||
let self_ty = cx
|
|
||||||
.tcx
|
|
||||||
.fn_sig(assoc_item.def_id)
|
|
||||||
.instantiate_identity()
|
|
||||||
.skip_binder()
|
|
||||||
.inputs()[0];
|
|
||||||
matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_mixed_projection_predicate<'tcx>(
|
|
||||||
cx: &LateContext<'tcx>,
|
|
||||||
callee_def_id: DefId,
|
|
||||||
projection_predicate: &ProjectionPredicate<'tcx>,
|
|
||||||
) -> bool {
|
|
||||||
let generics = cx.tcx.generics_of(callee_def_id);
|
|
||||||
// The predicate requires the projected type to equal a type parameter from the parent context.
|
|
||||||
if let Some(term_ty) = projection_predicate.term.ty()
|
|
||||||
&& let ty::Param(term_param_ty) = term_ty.kind()
|
|
||||||
&& (term_param_ty.index as usize) < generics.parent_count
|
|
||||||
{
|
|
||||||
// The inner-most self type is a type parameter from the current function.
|
|
||||||
let mut projection_ty = projection_predicate.projection_ty;
|
|
||||||
loop {
|
|
||||||
match projection_ty.self_ty().kind() {
|
|
||||||
ty::Alias(ty::Projection, inner_projection_ty) => {
|
|
||||||
projection_ty = *inner_projection_ty;
|
|
||||||
}
|
|
||||||
ty::Param(param_ty) => {
|
|
||||||
return (param_ty.index as usize) >= generics.parent_count;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn referent_used_exactly_once<'tcx>(
|
|
||||||
cx: &LateContext<'tcx>,
|
|
||||||
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
|
||||||
reference: &Expr<'tcx>,
|
|
||||||
) -> bool {
|
|
||||||
if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id)
|
|
||||||
&& let Some(local) = expr_local(cx.tcx, reference)
|
|
||||||
&& let [location] = *local_assignments(mir, local).as_slice()
|
|
||||||
&& let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index)
|
|
||||||
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
|
|
||||||
&& !place.is_indirect_first_projection()
|
|
||||||
// Ensure not in a loop (https://github.com/rust-lang/rust-clippy/issues/9710)
|
|
||||||
&& TriColorDepthFirstSearch::new(&mir.basic_blocks).run_from(location.block, &mut CycleDetector).is_none()
|
|
||||||
{
|
|
||||||
let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
|
|
||||||
if possible_borrowers
|
|
||||||
.last()
|
|
||||||
.map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
|
|
||||||
{
|
|
||||||
possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
|
|
||||||
}
|
|
||||||
let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
|
|
||||||
// If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
|
|
||||||
// that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
|
|
||||||
// itself. See the comment in that method for an explanation as to why.
|
|
||||||
possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
|
|
||||||
&& used_exactly_once(mir, place.local).unwrap_or(false)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
|
|
||||||
// projected type that is a type parameter. Returns `false` if replacing the types would have an
|
|
||||||
// effect on the function signature beyond substituting `new_ty` for `param_ty`.
|
|
||||||
// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
|
|
||||||
fn replace_types<'tcx>(
|
|
||||||
cx: &LateContext<'tcx>,
|
|
||||||
param_ty: ParamTy,
|
|
||||||
new_ty: Ty<'tcx>,
|
|
||||||
fn_sig: FnSig<'tcx>,
|
|
||||||
arg_index: usize,
|
|
||||||
projection_predicates: &[ProjectionPredicate<'tcx>],
|
|
||||||
args: &mut [ty::GenericArg<'tcx>],
|
|
||||||
) -> bool {
|
|
||||||
let mut replaced = BitSet::new_empty(args.len());
|
|
||||||
|
|
||||||
let mut deque = VecDeque::with_capacity(args.len());
|
|
||||||
deque.push_back((param_ty, new_ty));
|
|
||||||
|
|
||||||
while let Some((param_ty, new_ty)) = deque.pop_front() {
|
|
||||||
// If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
|
|
||||||
if !fn_sig
|
|
||||||
.inputs_and_output
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
args[param_ty.index as usize] = ty::GenericArg::from(new_ty);
|
|
||||||
|
|
||||||
// The `replaced.insert(...)` check provides some protection against infinite loops.
|
|
||||||
if replaced.insert(param_ty.index) {
|
|
||||||
for projection_predicate in projection_predicates {
|
|
||||||
if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
|
|
||||||
&& let Some(term_ty) = projection_predicate.term.ty()
|
|
||||||
&& let ty::Param(term_param_ty) = term_ty.kind()
|
|
||||||
{
|
|
||||||
let projection = cx.tcx.mk_ty_from_kind(ty::Alias(
|
|
||||||
ty::Projection,
|
|
||||||
projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty),
|
|
||||||
));
|
|
||||||
|
|
||||||
if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
|
|
||||||
&& args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
|
|
||||||
{
|
|
||||||
deque.push_back((*term_param_ty, projected_ty));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
|
fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
|
||||||
if let ty::Adt(adt, _) = *ty.kind() {
|
if let ty::Adt(adt, _) = *ty.kind() {
|
||||||
adt.is_struct() && adt.all_fields().any(|f| f.name == name)
|
adt.is_struct() && adt.all_fields().any(|f| f.name == name)
|
||||||
|
@ -167,7 +167,10 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let first = &def.variants[0].ident.name.as_str();
|
let first = match def.variants.first() {
|
||||||
|
Some(variant) => variant.ident.name.as_str(),
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
let mut pre = camel_case_split(first);
|
let mut pre = camel_case_split(first);
|
||||||
let mut post = pre.clone();
|
let mut post = pre.clone();
|
||||||
post.reverse();
|
post.reverse();
|
||||||
|
@ -3,7 +3,6 @@ use clippy_utils::path_res;
|
|||||||
use clippy_utils::ty::implements_trait;
|
use clippy_utils::ty::implements_trait;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_hir::{Item, ItemKind};
|
use rustc_hir::{Item, ItemKind};
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::Visibility;
|
use rustc_middle::ty::Visibility;
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
@ -42,9 +41,10 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match item.kind {
|
match item.kind {
|
||||||
ItemKind::TyAlias(ty, _) if implements_trait(cx, hir_ty_to_ty(cx.tcx, ty), error_def_id, &[])
|
ItemKind::TyAlias(..) if item.ident.name == sym::Error
|
||||||
&& item.ident.name == sym::Error
|
&& is_visible_outside_module(cx, item.owner_id.def_id)
|
||||||
&& is_visible_outside_module(cx, item.owner_id.def_id) =>
|
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
|
||||||
|
&& implements_trait(cx, ty, error_def_id, &[]) =>
|
||||||
{
|
{
|
||||||
span_lint(
|
span_lint(
|
||||||
cx,
|
cx,
|
||||||
|
@ -57,54 +57,52 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
&& let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root())
|
||||||
{
|
{
|
||||||
find_format_args(cx, write_arg, ExpnId::root(), |format_args| {
|
// ordering is important here, since `writeln!` uses `write!` internally
|
||||||
let calling_macro =
|
let calling_macro = if is_expn_of(write_call.span, "writeln").is_some() {
|
||||||
// ordering is important here, since `writeln!` uses `write!` internally
|
Some("writeln")
|
||||||
if is_expn_of(write_call.span, "writeln").is_some() {
|
} else if is_expn_of(write_call.span, "write").is_some() {
|
||||||
Some("writeln")
|
Some("write")
|
||||||
} else if is_expn_of(write_call.span, "write").is_some() {
|
} else {
|
||||||
Some("write")
|
None
|
||||||
} else {
|
};
|
||||||
None
|
let prefix = if dest_name == "stderr" {
|
||||||
};
|
"e"
|
||||||
let prefix = if dest_name == "stderr" {
|
} else {
|
||||||
"e"
|
""
|
||||||
} else {
|
};
|
||||||
""
|
|
||||||
};
|
|
||||||
|
|
||||||
// We need to remove the last trailing newline from the string because the
|
// We need to remove the last trailing newline from the string because the
|
||||||
// underlying `fmt::write` function doesn't know whether `println!` or `print!` was
|
// underlying `fmt::write` function doesn't know whether `println!` or `print!` was
|
||||||
// used.
|
// used.
|
||||||
let (used, sugg_mac) = if let Some(macro_name) = calling_macro {
|
let (used, sugg_mac) = if let Some(macro_name) = calling_macro {
|
||||||
(
|
(
|
||||||
format!("{macro_name}!({dest_name}(), ...)"),
|
format!("{macro_name}!({dest_name}(), ...)"),
|
||||||
macro_name.replace("write", "print"),
|
macro_name.replace("write", "print"),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
format!("{dest_name}().write_fmt(...)"),
|
format!("{dest_name}().write_fmt(...)"),
|
||||||
"print".into(),
|
"print".into(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let inputs_snippet = snippet_with_applicability(
|
let inputs_snippet = snippet_with_applicability(
|
||||||
cx,
|
cx,
|
||||||
format_args_inputs_span(format_args),
|
format_args_inputs_span(&format_args),
|
||||||
"..",
|
"..",
|
||||||
&mut applicability,
|
&mut applicability,
|
||||||
);
|
);
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
EXPLICIT_WRITE,
|
EXPLICIT_WRITE,
|
||||||
expr.span,
|
expr.span,
|
||||||
&format!("use of `{used}.unwrap()`"),
|
&format!("use of `{used}.unwrap()`"),
|
||||||
"try",
|
"try",
|
||||||
format!("{prefix}{sugg_mac}!({inputs_snippet})"),
|
format!("{prefix}{sugg_mac}!({inputs_snippet})"),
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,8 +246,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
|
|||||||
{
|
{
|
||||||
self.ty_params.remove(&def_id);
|
self.ty_params.remove(&def_id);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// If the bounded type isn't a generic param, but is instead a concrete generic
|
||||||
|
// type, any params we find nested inside of it are being used as concrete types,
|
||||||
|
// and can therefore can be considered used. So, we're fine to walk the left-hand
|
||||||
|
// side of the where bound.
|
||||||
|
walk_ty(self, predicate.bounded_ty);
|
||||||
}
|
}
|
||||||
// Only walk the right-hand side of where bounds
|
|
||||||
for bound in predicate.bounds {
|
for bound in predicate.bounds {
|
||||||
walk_param_bound(self, bound);
|
walk_param_bound(self, bound);
|
||||||
}
|
}
|
||||||
|
@ -43,14 +43,10 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
|
|||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
|
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||||
return;
|
&& cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id)
|
||||||
};
|
&& let Some(format_args) = find_format_args(cx, expr, macro_call.expn)
|
||||||
if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
|
{
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
find_format_args(cx, expr, macro_call.expn, |format_args| {
|
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let call_site = macro_call.span;
|
let call_site = macro_call.span;
|
||||||
|
|
||||||
@ -91,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
|||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,15 +186,10 @@ impl FormatArgs {
|
|||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
|
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||||
return;
|
&& is_format_macro(cx, macro_call.def_id)
|
||||||
};
|
&& let Some(format_args) = find_format_args(cx, expr, macro_call.expn)
|
||||||
if !is_format_macro(cx, macro_call.def_id) {
|
{
|
||||||
return;
|
|
||||||
}
|
|
||||||
let name = cx.tcx.item_name(macro_call.def_id);
|
|
||||||
|
|
||||||
find_format_args(cx, expr, macro_call.expn, |format_args| {
|
|
||||||
for piece in &format_args.template {
|
for piece in &format_args.template {
|
||||||
if let FormatArgsPiece::Placeholder(placeholder) = piece
|
if let FormatArgsPiece::Placeholder(placeholder) = piece
|
||||||
&& let Ok(index) = placeholder.argument.index
|
&& let Ok(index) = placeholder.argument.index
|
||||||
@ -206,12 +201,13 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
|||||||
|
|
||||||
if placeholder.format_trait != FormatTrait::Display
|
if placeholder.format_trait != FormatTrait::Display
|
||||||
|| placeholder.format_options != FormatOptions::default()
|
|| placeholder.format_options != FormatOptions::default()
|
||||||
|| is_aliased(format_args, index)
|
|| is_aliased(&format_args, index)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(arg_hir_expr) = arg_expr {
|
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_format_in_format_args(cx, macro_call.span, name, arg_hir_expr);
|
||||||
check_to_string_in_format_args(cx, name, arg_hir_expr);
|
check_to_string_in_format_args(cx, name, arg_hir_expr);
|
||||||
}
|
}
|
||||||
@ -219,9 +215,9 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
|
if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
|
||||||
check_uninlined_args(cx, format_args, macro_call.span, macro_call.def_id, self.ignore_mixed);
|
check_uninlined_args(cx, &format_args, macro_call.span, macro_call.def_id, self.ignore_mixed);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extract_msrv_attr!(LateContext);
|
extract_msrv_attr!(LateContext);
|
||||||
|
@ -170,30 +170,29 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
|
|||||||
if let Some(outer_macro) = root_macro_call_first_node(cx, expr)
|
if let Some(outer_macro) = root_macro_call_first_node(cx, expr)
|
||||||
&& let macro_def_id = outer_macro.def_id
|
&& let macro_def_id = outer_macro.def_id
|
||||||
&& is_format_macro(cx, macro_def_id)
|
&& is_format_macro(cx, macro_def_id)
|
||||||
|
&& let Some(format_args) = find_format_args(cx, expr, outer_macro.expn)
|
||||||
{
|
{
|
||||||
find_format_args(cx, expr, outer_macro.expn, |format_args| {
|
for piece in &format_args.template {
|
||||||
for piece in &format_args.template {
|
if let FormatArgsPiece::Placeholder(placeholder) = piece
|
||||||
if let FormatArgsPiece::Placeholder(placeholder) = piece
|
&& let trait_name = match placeholder.format_trait {
|
||||||
&& let trait_name = match placeholder.format_trait {
|
FormatTrait::Display => sym::Display,
|
||||||
FormatTrait::Display => sym::Display,
|
FormatTrait::Debug => sym::Debug,
|
||||||
FormatTrait::Debug => sym::Debug,
|
FormatTrait::LowerExp => sym!(LowerExp),
|
||||||
FormatTrait::LowerExp => sym!(LowerExp),
|
FormatTrait::UpperExp => sym!(UpperExp),
|
||||||
FormatTrait::UpperExp => sym!(UpperExp),
|
FormatTrait::Octal => sym!(Octal),
|
||||||
FormatTrait::Octal => sym!(Octal),
|
FormatTrait::Pointer => sym::Pointer,
|
||||||
FormatTrait::Pointer => sym::Pointer,
|
FormatTrait::Binary => sym!(Binary),
|
||||||
FormatTrait::Binary => sym!(Binary),
|
FormatTrait::LowerHex => sym!(LowerHex),
|
||||||
FormatTrait::LowerHex => sym!(LowerHex),
|
FormatTrait::UpperHex => sym!(UpperHex),
|
||||||
FormatTrait::UpperHex => sym!(UpperHex),
|
|
||||||
}
|
|
||||||
&& 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);
|
|
||||||
}
|
}
|
||||||
|
&& 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);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Item, ItemKind};
|
use rustc_hir::{Item, ItemKind};
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::layout::LayoutOf;
|
use rustc_middle::ty::layout::LayoutOf;
|
||||||
use rustc_middle::ty::{self, ConstKind};
|
use rustc_middle::ty::{self, ConstKind};
|
||||||
@ -50,12 +49,12 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
|
|||||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if !item.span.from_expansion();
|
if !item.span.from_expansion();
|
||||||
if let ItemKind::Const(hir_ty, generics, _) = &item.kind;
|
if let ItemKind::Const(_, generics, _) = &item.kind;
|
||||||
// Since static items may not have generics, skip generic const items.
|
// Since static items may not have generics, skip generic const items.
|
||||||
// FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it
|
// FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it
|
||||||
// doesn't account for empty where-clauses that only consist of keyword `where` IINM.
|
// doesn't account for empty where-clauses that only consist of keyword `where` IINM.
|
||||||
if generics.params.is_empty() && !generics.has_where_clause_predicates;
|
if generics.params.is_empty() && !generics.has_where_clause_predicates;
|
||||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
|
||||||
if let ty::Array(element_type, cst) = ty.kind();
|
if let ty::Array(element_type, cst) = ty.kind();
|
||||||
if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind();
|
if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind();
|
||||||
if let Ok(element_count) = element_count.try_to_target_usize(cx.tcx);
|
if let Ok(element_count) = element_count.try_to_target_usize(cx.tcx);
|
||||||
|
@ -17,26 +17,20 @@ declare_clippy_lint! {
|
|||||||
///
|
///
|
||||||
/// ### Example
|
/// ### Example
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// async fn wait(f: impl std::future::Future<Output = ()>) {}
|
/// async fn large_future(_x: [u8; 16 * 1024]) {}
|
||||||
///
|
///
|
||||||
/// async fn big_fut(arg: [u8; 1024]) {}
|
/// pub async fn trigger() {
|
||||||
///
|
/// large_future([0u8; 16 * 1024]).await;
|
||||||
/// pub async fn test() {
|
|
||||||
/// let fut = big_fut([0u8; 1024]);
|
|
||||||
/// wait(fut).await;
|
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// `Box::pin` the big future instead.
|
/// `Box::pin` the big future instead.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// async fn wait(f: impl std::future::Future<Output = ()>) {}
|
/// async fn large_future(_x: [u8; 16 * 1024]) {}
|
||||||
///
|
///
|
||||||
/// async fn big_fut(arg: [u8; 1024]) {}
|
/// pub async fn trigger() {
|
||||||
///
|
/// Box::pin(large_future([0u8; 16 * 1024])).await;
|
||||||
/// pub async fn test() {
|
|
||||||
/// let fut = Box::pin(big_fut([0u8; 1024]));
|
|
||||||
/// wait(fut).await;
|
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.70.0"]
|
#[clippy::version = "1.70.0"]
|
||||||
|
@ -424,6 +424,14 @@ fn check_for_is_empty(
|
|||||||
item_name: Symbol,
|
item_name: Symbol,
|
||||||
item_kind: &str,
|
item_kind: &str,
|
||||||
) {
|
) {
|
||||||
|
// Implementor may be a type alias, in which case we need to get the `DefId` of the aliased type to
|
||||||
|
// find the correct inherent impls.
|
||||||
|
let impl_ty = if let Some(adt) = cx.tcx.type_of(impl_ty).skip_binder().ty_adt_def() {
|
||||||
|
adt.did()
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let is_empty = Symbol::intern("is_empty");
|
let is_empty = Symbol::intern("is_empty");
|
||||||
let is_empty = cx
|
let is_empty = cx
|
||||||
.tcx
|
.tcx
|
||||||
|
@ -230,6 +230,7 @@ mod mutex_atomic;
|
|||||||
mod needless_arbitrary_self_type;
|
mod needless_arbitrary_self_type;
|
||||||
mod needless_bool;
|
mod needless_bool;
|
||||||
mod needless_borrowed_ref;
|
mod needless_borrowed_ref;
|
||||||
|
mod needless_borrows_for_generic_args;
|
||||||
mod needless_continue;
|
mod needless_continue;
|
||||||
mod needless_else;
|
mod needless_else;
|
||||||
mod needless_for_each;
|
mod needless_for_each;
|
||||||
@ -331,6 +332,7 @@ mod unit_return_expecting_ord;
|
|||||||
mod unit_types;
|
mod unit_types;
|
||||||
mod unnamed_address;
|
mod unnamed_address;
|
||||||
mod unnecessary_box_returns;
|
mod unnecessary_box_returns;
|
||||||
|
mod unnecessary_map_on_constructor;
|
||||||
mod unnecessary_owned_empty_strings;
|
mod unnecessary_owned_empty_strings;
|
||||||
mod unnecessary_self_imports;
|
mod unnecessary_self_imports;
|
||||||
mod unnecessary_struct_initialization;
|
mod unnecessary_struct_initialization;
|
||||||
@ -610,7 +612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
.collect(),
|
.collect(),
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector));
|
store.register_early_pass(|| Box::<utils::format_args_collector::FormatArgsCollector>::default());
|
||||||
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
|
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
|
||||||
store.register_late_pass(|_| Box::new(utils::author::Author));
|
store.register_late_pass(|_| Box::new(utils::author::Author));
|
||||||
let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
|
let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
|
||||||
@ -637,7 +639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool));
|
store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool));
|
||||||
store.register_late_pass(|_| Box::new(needless_bool::BoolComparison));
|
store.register_late_pass(|_| Box::new(needless_bool::BoolComparison));
|
||||||
store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach));
|
store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach));
|
||||||
store.register_late_pass(|_| Box::<misc::LintPass>::default());
|
store.register_late_pass(|_| Box::new(misc::LintPass));
|
||||||
store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction));
|
store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction));
|
||||||
store.register_late_pass(|_| Box::new(mut_mut::MutMut));
|
store.register_late_pass(|_| Box::new(mut_mut::MutMut));
|
||||||
store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed));
|
store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed));
|
||||||
@ -663,12 +665,19 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
|
let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
|
||||||
let suppress_restriction_lint_in_const = conf.suppress_restriction_lint_in_const;
|
let suppress_restriction_lint_in_const = conf.suppress_restriction_lint_in_const;
|
||||||
store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv())));
|
store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv())));
|
||||||
|
let allowed_dotfiles = conf
|
||||||
|
.allowed_dotfiles
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.chain(methods::DEFAULT_ALLOWED_DOTFILES.iter().copied().map(ToOwned::to_owned))
|
||||||
|
.collect::<FxHashSet<_>>();
|
||||||
store.register_late_pass(move |_| {
|
store.register_late_pass(move |_| {
|
||||||
Box::new(methods::Methods::new(
|
Box::new(methods::Methods::new(
|
||||||
avoid_breaking_exported_api,
|
avoid_breaking_exported_api,
|
||||||
msrv(),
|
msrv(),
|
||||||
allow_expect_in_tests,
|
allow_expect_in_tests,
|
||||||
allow_unwrap_in_tests,
|
allow_unwrap_in_tests,
|
||||||
|
allowed_dotfiles.clone(),
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv())));
|
store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv())));
|
||||||
@ -881,7 +890,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
|
store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
|
||||||
store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
|
store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
|
||||||
store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
|
store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
|
||||||
store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv())));
|
store.register_late_pass(|_| Box::<dereference::Dereferencing<'_>>::default());
|
||||||
store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
|
store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
|
||||||
store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
|
store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
|
||||||
let future_size_threshold = conf.future_size_threshold;
|
let future_size_threshold = conf.future_size_threshold;
|
||||||
@ -1104,6 +1113,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default());
|
store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default());
|
||||||
store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls));
|
store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls));
|
||||||
store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing));
|
store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing));
|
||||||
|
store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor));
|
||||||
|
store.register_late_pass(move |_| {
|
||||||
|
Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new(
|
||||||
|
msrv(),
|
||||||
|
))
|
||||||
|
});
|
||||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ use rustc_ast::ast::{LitIntType, LitKind};
|
|||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor};
|
use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor};
|
||||||
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt};
|
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt};
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
@ -150,7 +149,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
|
|||||||
if l.pat.hir_id == self.var_id;
|
if l.pat.hir_id == self.var_id;
|
||||||
if let PatKind::Binding(.., ident, _) = l.pat.kind;
|
if let PatKind::Binding(.., ident, _) = l.pat.kind;
|
||||||
then {
|
then {
|
||||||
let ty = l.ty.map(|ty| hir_ty_to_ty(self.cx.tcx, ty));
|
let ty = l.ty.map(|_| self.cx.typeck_results().pat_ty(l.pat));
|
||||||
|
|
||||||
self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| {
|
self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| {
|
||||||
InitializeVisitorState::Initialized {
|
InitializeVisitorState::Initialized {
|
||||||
|
@ -36,7 +36,8 @@ struct PathAndSpan {
|
|||||||
span: Span,
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MacroRefData` includes the name of the macro.
|
/// `MacroRefData` includes the name of the macro
|
||||||
|
/// and the path from `SourceMap::span_to_filename`.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MacroRefData {
|
pub struct MacroRefData {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -8,8 +8,7 @@ use clippy_utils::{
|
|||||||
};
|
};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::LangItem::OptionNone;
|
use rustc_hir::LangItem::OptionNone;
|
||||||
use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath};
|
use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, Guard, ItemKind, Node, Pat, PatKind, Path, QPath};
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
|
|
||||||
@ -141,11 +140,15 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>
|
|||||||
return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr));
|
return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr));
|
||||||
},
|
},
|
||||||
// compare match_expr ty with RetTy in `fn foo() -> RetTy`
|
// compare match_expr ty with RetTy in `fn foo() -> RetTy`
|
||||||
Node::Item(..) => {
|
Node::Item(item) => {
|
||||||
if let Some(fn_decl) = p_node.fn_decl() {
|
if let ItemKind::Fn(..) = item.kind {
|
||||||
if let FnRetTy::Return(ret_ty) = fn_decl.output {
|
let output = cx
|
||||||
return same_type_and_consts(hir_ty_to_ty(cx.tcx, ret_ty), cx.typeck_results().expr_ty(expr));
|
.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 {..} }`
|
// check the parent expr for this whole block `{ match match_expr {..} }`
|
||||||
|
@ -2,11 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||||||
use clippy_utils::path_to_local;
|
use clippy_utils::path_to_local;
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::visitors::{for_each_expr, is_local_used};
|
use clippy_utils::visitors::{for_each_expr, is_local_used};
|
||||||
use rustc_ast::LitKind;
|
use rustc_ast::{BorrowKind, LitKind};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, Guard, MatchSource, Node, Pat, PatKind};
|
use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, Guard, MatchSource, Node, Pat, PatKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
@ -34,32 +35,45 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
|
|||||||
],
|
],
|
||||||
MatchSource::Normal,
|
MatchSource::Normal,
|
||||||
) = if_expr.kind
|
) = if_expr.kind
|
||||||
|
&& let Some(binding) = get_pat_binding(cx, scrutinee, outer_arm)
|
||||||
{
|
{
|
||||||
|
let pat_span = match (arm.pat.kind, binding.byref_ident) {
|
||||||
|
(PatKind::Ref(pat, _), Some(_)) => pat.span,
|
||||||
|
(PatKind::Ref(..), None) | (_, Some(_)) => continue,
|
||||||
|
_ => arm.pat.span,
|
||||||
|
};
|
||||||
emit_redundant_guards(
|
emit_redundant_guards(
|
||||||
cx,
|
cx,
|
||||||
outer_arm,
|
outer_arm,
|
||||||
if_expr.span,
|
if_expr.span,
|
||||||
scrutinee,
|
pat_span,
|
||||||
arm.pat.span,
|
&binding,
|
||||||
arm.guard,
|
arm.guard,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// `Some(x) if let Some(2) = x`
|
// `Some(x) if let Some(2) = x`
|
||||||
else if let Guard::IfLet(let_expr) = guard {
|
else if let Guard::IfLet(let_expr) = guard
|
||||||
|
&& let Some(binding) = get_pat_binding(cx, let_expr.init, outer_arm)
|
||||||
|
{
|
||||||
|
let pat_span = match (let_expr.pat.kind, binding.byref_ident) {
|
||||||
|
(PatKind::Ref(pat, _), Some(_)) => pat.span,
|
||||||
|
(PatKind::Ref(..), None) | (_, Some(_)) => continue,
|
||||||
|
_ => let_expr.pat.span,
|
||||||
|
};
|
||||||
emit_redundant_guards(
|
emit_redundant_guards(
|
||||||
cx,
|
cx,
|
||||||
outer_arm,
|
outer_arm,
|
||||||
let_expr.span,
|
let_expr.span,
|
||||||
let_expr.init,
|
pat_span,
|
||||||
let_expr.pat.span,
|
&binding,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// `Some(x) if x == Some(2)`
|
// `Some(x) if x == Some(2)`
|
||||||
|
// `Some(x) if Some(2) == x`
|
||||||
else if let Guard::If(if_expr) = guard
|
else if let Guard::If(if_expr) = guard
|
||||||
&& let ExprKind::Binary(bin_op, local, pat) = if_expr.kind
|
&& let ExprKind::Binary(bin_op, local, pat) = if_expr.kind
|
||||||
&& matches!(bin_op.node, BinOpKind::Eq)
|
&& matches!(bin_op.node, BinOpKind::Eq)
|
||||||
&& expr_can_be_pat(cx, pat)
|
|
||||||
// Ensure they have the same type. If they don't, we'd need deref coercion which isn't
|
// Ensure they have the same type. If they don't, we'd need deref coercion which isn't
|
||||||
// possible (currently) in a pattern. In some cases, you can use something like
|
// possible (currently) in a pattern. In some cases, you can use something like
|
||||||
// `as_deref` or similar but in general, we shouldn't lint this as it'd create an
|
// `as_deref` or similar but in general, we shouldn't lint this as it'd create an
|
||||||
@ -67,43 +81,68 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
|
|||||||
//
|
//
|
||||||
// This isn't necessary in the other two checks, as they must be a pattern already.
|
// This isn't necessary in the other two checks, as they must be a pattern already.
|
||||||
&& cx.typeck_results().expr_ty(local) == cx.typeck_results().expr_ty(pat)
|
&& cx.typeck_results().expr_ty(local) == cx.typeck_results().expr_ty(pat)
|
||||||
|
// Since we want to lint on both `x == Some(2)` and `Some(2) == x`, we might have to "swap"
|
||||||
|
// `local` and `pat`, depending on which side they are.
|
||||||
|
&& let Some((binding, pat)) = get_pat_binding(cx, local, outer_arm)
|
||||||
|
.map(|binding| (binding, pat))
|
||||||
|
.or_else(|| get_pat_binding(cx, pat, outer_arm).map(|binding| (binding, local)))
|
||||||
|
&& expr_can_be_pat(cx, pat)
|
||||||
{
|
{
|
||||||
|
let pat_span = match (pat.kind, binding.byref_ident) {
|
||||||
|
(ExprKind::AddrOf(BorrowKind::Ref, _, expr), Some(_)) => expr.span,
|
||||||
|
(ExprKind::AddrOf(..), None) | (_, Some(_)) => continue,
|
||||||
|
_ => pat.span,
|
||||||
|
};
|
||||||
emit_redundant_guards(
|
emit_redundant_guards(
|
||||||
cx,
|
cx,
|
||||||
outer_arm,
|
outer_arm,
|
||||||
if_expr.span,
|
if_expr.span,
|
||||||
local,
|
pat_span,
|
||||||
pat.span,
|
&binding,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pat_binding<'tcx>(cx: &LateContext<'tcx>, guard_expr: &Expr<'_>, outer_arm: &Arm<'tcx>) -> Option<(Span, bool)> {
|
struct PatBindingInfo {
|
||||||
|
span: Span,
|
||||||
|
byref_ident: Option<Ident>,
|
||||||
|
is_field: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_pat_binding<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
guard_expr: &Expr<'_>,
|
||||||
|
outer_arm: &Arm<'tcx>,
|
||||||
|
) -> Option<PatBindingInfo> {
|
||||||
if let Some(local) = path_to_local(guard_expr) && !is_local_used(cx, outer_arm.body, local) {
|
if let Some(local) = path_to_local(guard_expr) && !is_local_used(cx, outer_arm.body, local) {
|
||||||
let mut span = None;
|
let mut span = None;
|
||||||
|
let mut byref_ident = None;
|
||||||
let mut multiple_bindings = false;
|
let mut multiple_bindings = false;
|
||||||
// `each_binding` gives the `HirId` of the `Pat` itself, not the binding
|
// `each_binding` gives the `HirId` of the `Pat` itself, not the binding
|
||||||
outer_arm.pat.walk(|pat| {
|
outer_arm.pat.walk(|pat| {
|
||||||
if let PatKind::Binding(_, hir_id, _, _) = pat.kind
|
if let PatKind::Binding(bind_annot, hir_id, ident, _) = pat.kind
|
||||||
&& hir_id == local
|
&& hir_id == local
|
||||||
&& span.replace(pat.span).is_some()
|
|
||||||
{
|
{
|
||||||
multiple_bindings = true;
|
if matches!(bind_annot.0, rustc_ast::ByRef::Yes) {
|
||||||
return false;
|
let _ = byref_ident.insert(ident);
|
||||||
|
}
|
||||||
|
// the second call of `replace()` returns a `Some(span)`, meaning a multi-binding pattern
|
||||||
|
if span.replace(pat.span).is_some() {
|
||||||
|
multiple_bindings = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ignore bindings from or patterns, like `First(x) | Second(x, _) | Third(x, _, _)`
|
// Ignore bindings from or patterns, like `First(x) | Second(x, _) | Third(x, _, _)`
|
||||||
if !multiple_bindings {
|
if !multiple_bindings {
|
||||||
return span.map(|span| {
|
return span.map(|span| PatBindingInfo {
|
||||||
(
|
span,
|
||||||
span,
|
byref_ident,
|
||||||
!matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)),
|
is_field: matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)),
|
||||||
)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,14 +154,11 @@ fn emit_redundant_guards<'tcx>(
|
|||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
outer_arm: &Arm<'tcx>,
|
outer_arm: &Arm<'tcx>,
|
||||||
guard_span: Span,
|
guard_span: Span,
|
||||||
local: &Expr<'_>,
|
|
||||||
pat_span: Span,
|
pat_span: Span,
|
||||||
|
pat_binding: &PatBindingInfo,
|
||||||
inner_guard: Option<Guard<'_>>,
|
inner_guard: Option<Guard<'_>>,
|
||||||
) {
|
) {
|
||||||
let mut app = Applicability::MaybeIncorrect;
|
let mut app = Applicability::MaybeIncorrect;
|
||||||
let Some((pat_binding, can_use_shorthand)) = get_pat_binding(cx, local, outer_arm) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
@ -131,14 +167,21 @@ fn emit_redundant_guards<'tcx>(
|
|||||||
"redundant guard",
|
"redundant guard",
|
||||||
|diag| {
|
|diag| {
|
||||||
let binding_replacement = snippet_with_applicability(cx, pat_span, "<binding_repl>", &mut app);
|
let binding_replacement = snippet_with_applicability(cx, pat_span, "<binding_repl>", &mut app);
|
||||||
|
let suggestion_span = match *pat_binding {
|
||||||
|
PatBindingInfo {
|
||||||
|
span,
|
||||||
|
byref_ident: Some(ident),
|
||||||
|
is_field: true,
|
||||||
|
} => (span, format!("{ident}: {binding_replacement}")),
|
||||||
|
PatBindingInfo {
|
||||||
|
span, is_field: true, ..
|
||||||
|
} => (span.shrink_to_hi(), format!(": {binding_replacement}")),
|
||||||
|
PatBindingInfo { span, .. } => (span, binding_replacement.into_owned()),
|
||||||
|
};
|
||||||
diag.multipart_suggestion_verbose(
|
diag.multipart_suggestion_verbose(
|
||||||
"try",
|
"try",
|
||||||
vec![
|
vec![
|
||||||
if can_use_shorthand {
|
suggestion_span,
|
||||||
(pat_binding, binding_replacement.into_owned())
|
|
||||||
} else {
|
|
||||||
(pat_binding.shrink_to_hi(), format!(": {binding_replacement}"))
|
|
||||||
},
|
|
||||||
(
|
(
|
||||||
guard_span.source_callsite().with_lo(outer_arm.pat.span.hi()),
|
guard_span.source_callsite().with_lo(outer_arm.pat.span.hi()),
|
||||||
inner_guard.map_or_else(String::new, |guard| {
|
inner_guard.map_or_else(String::new, |guard| {
|
||||||
|
@ -131,13 +131,12 @@ pub(super) fn check<'tcx>(
|
|||||||
|
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
|
||||||
//Special handling for `format!` as arg_root
|
// Special handling for `format!` as arg_root
|
||||||
if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) {
|
if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) {
|
||||||
if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
|
if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id)
|
||||||
return;
|
&& let Some(format_args) = find_format_args(cx, arg_root, macro_call.expn)
|
||||||
}
|
{
|
||||||
find_format_args(cx, arg_root, macro_call.expn, |format_args| {
|
let span = format_args_inputs_span(&format_args);
|
||||||
let span = format_args_inputs_span(format_args);
|
|
||||||
let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
|
let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
@ -148,7 +147,7 @@ pub(super) fn check<'tcx>(
|
|||||||
format!("unwrap_or_else({closure_args} panic!({sugg}))"),
|
format!("unwrap_or_else({closure_args} panic!({sugg}))"),
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ use rustc_errors::Applicability;
|
|||||||
use rustc_hir::{Expr, ExprKind};
|
use rustc_hir::{Expr, ExprKind};
|
||||||
use rustc_lint::{LateContext, LintContext};
|
use rustc_lint::{LateContext, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
|
use rustc_middle::ty::adjustment::Adjust;
|
||||||
use rustc_middle::ty::Binder;
|
use rustc_middle::ty::Binder;
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
@ -36,6 +37,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
|
|||||||
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id)
|
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id)
|
||||||
&& match_def_path(cx, def_id, &BOOL_THEN)
|
&& match_def_path(cx, def_id, &BOOL_THEN)
|
||||||
&& !is_from_proc_macro(cx, expr)
|
&& !is_from_proc_macro(cx, expr)
|
||||||
|
// Count the number of derefs needed to get to the bool because we need those in the suggestion
|
||||||
|
&& let needed_derefs = cx.typeck_results().expr_adjustments(recv)
|
||||||
|
.iter()
|
||||||
|
.filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
|
||||||
|
.count()
|
||||||
&& let Some(param_snippet) = snippet_opt(cx, param.span)
|
&& let Some(param_snippet) = snippet_opt(cx, param.span)
|
||||||
&& let Some(filter) = snippet_opt(cx, recv.span)
|
&& let Some(filter) = snippet_opt(cx, recv.span)
|
||||||
&& let Some(map) = snippet_opt(cx, then_body.span)
|
&& let Some(map) = snippet_opt(cx, then_body.span)
|
||||||
@ -46,7 +52,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
|
|||||||
call_span,
|
call_span,
|
||||||
"usage of `bool::then` in `filter_map`",
|
"usage of `bool::then` in `filter_map`",
|
||||||
"use `filter` then `map` instead",
|
"use `filter` then `map` instead",
|
||||||
format!("filter(|&{param_snippet}| {filter}).map(|{param_snippet}| {map})"),
|
format!(
|
||||||
|
"filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})",
|
||||||
|
derefs="*".repeat(needed_derefs)
|
||||||
|
),
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -74,9 +74,11 @@ mod option_map_unwrap_or;
|
|||||||
mod or_fun_call;
|
mod or_fun_call;
|
||||||
mod or_then_unwrap;
|
mod or_then_unwrap;
|
||||||
mod path_buf_push_overwrite;
|
mod path_buf_push_overwrite;
|
||||||
|
mod path_ends_with_ext;
|
||||||
mod range_zip_with_len;
|
mod range_zip_with_len;
|
||||||
mod read_line_without_trim;
|
mod read_line_without_trim;
|
||||||
mod readonly_write_lock;
|
mod readonly_write_lock;
|
||||||
|
mod redundant_as_str;
|
||||||
mod repeat_once;
|
mod repeat_once;
|
||||||
mod search_is_some;
|
mod search_is_some;
|
||||||
mod seek_from_current;
|
mod seek_from_current;
|
||||||
@ -120,9 +122,10 @@ use clippy_utils::msrvs::{self, Msrv};
|
|||||||
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
|
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
|
||||||
use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty};
|
use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty};
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
|
pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES;
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind};
|
use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind};
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_middle::ty::{self, TraitRef, Ty};
|
use rustc_middle::ty::{self, TraitRef, Ty};
|
||||||
@ -3563,11 +3566,77 @@ declare_clippy_lint! {
|
|||||||
"calls to `.take()` or `.skip()` that are out of bounds"
|
"calls to `.take()` or `.skip()` that are out of bounds"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Looks for calls to `Path::ends_with` calls where the argument looks like a file extension.
|
||||||
|
///
|
||||||
|
/// By default, Clippy has a short list of known filenames that start with a dot
|
||||||
|
/// but aren't necessarily file extensions (e.g. the `.git` folder), which are allowed by default.
|
||||||
|
/// The `allowed-dotfiles` configuration can be used to allow additional
|
||||||
|
/// file extensions that Clippy should not lint.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// This doesn't actually compare file extensions. Rather, `ends_with` compares the given argument
|
||||||
|
/// to the last **component** of the path and checks if it matches exactly.
|
||||||
|
///
|
||||||
|
/// ### Known issues
|
||||||
|
/// File extensions are often at most three characters long, so this only lints in those cases
|
||||||
|
/// in an attempt to avoid false positives.
|
||||||
|
/// Any extension names longer than that are assumed to likely be real path components and are
|
||||||
|
/// therefore ignored.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// # use std::path::Path;
|
||||||
|
/// fn is_markdown(path: &Path) -> bool {
|
||||||
|
/// path.ends_with(".md")
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// # use std::path::Path;
|
||||||
|
/// fn is_markdown(path: &Path) -> bool {
|
||||||
|
/// path.extension().is_some_and(|ext| ext == "md")
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.74.0"]
|
||||||
|
pub PATH_ENDS_WITH_EXT,
|
||||||
|
suspicious,
|
||||||
|
"attempting to compare file extensions using `Path::ends_with`"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for usage of `as_str()` on a `String`` chained with a method available on the `String` itself.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// The `as_str()` conversion is pointless and can be removed for simplicity and cleanliness.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// # #![allow(unused)]
|
||||||
|
/// let owned_string = "This is a string".to_owned();
|
||||||
|
/// owned_string.as_str().as_bytes();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// # #![allow(unused)]
|
||||||
|
/// let owned_string = "This is a string".to_owned();
|
||||||
|
/// owned_string.as_bytes();
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.74.0"]
|
||||||
|
pub REDUNDANT_AS_STR,
|
||||||
|
complexity,
|
||||||
|
"`as_str` used to call a method on `str` that is also available on `String`"
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Methods {
|
pub struct Methods {
|
||||||
avoid_breaking_exported_api: bool,
|
avoid_breaking_exported_api: bool,
|
||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
allow_expect_in_tests: bool,
|
allow_expect_in_tests: bool,
|
||||||
allow_unwrap_in_tests: bool,
|
allow_unwrap_in_tests: bool,
|
||||||
|
allowed_dotfiles: FxHashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Methods {
|
impl Methods {
|
||||||
@ -3577,12 +3646,14 @@ impl Methods {
|
|||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
allow_expect_in_tests: bool,
|
allow_expect_in_tests: bool,
|
||||||
allow_unwrap_in_tests: bool,
|
allow_unwrap_in_tests: bool,
|
||||||
|
allowed_dotfiles: FxHashSet<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
avoid_breaking_exported_api,
|
avoid_breaking_exported_api,
|
||||||
msrv,
|
msrv,
|
||||||
allow_expect_in_tests,
|
allow_expect_in_tests,
|
||||||
allow_unwrap_in_tests,
|
allow_unwrap_in_tests,
|
||||||
|
allowed_dotfiles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3703,6 +3774,8 @@ impl_lint_pass!(Methods => [
|
|||||||
FILTER_MAP_BOOL_THEN,
|
FILTER_MAP_BOOL_THEN,
|
||||||
READONLY_WRITE_LOCK,
|
READONLY_WRITE_LOCK,
|
||||||
ITER_OUT_OF_BOUNDS,
|
ITER_OUT_OF_BOUNDS,
|
||||||
|
PATH_ENDS_WITH_EXT,
|
||||||
|
REDUNDANT_AS_STR,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/// Extracts a method call name, args, and `Span` of the method name.
|
/// Extracts a method call name, args, and `Span` of the method name.
|
||||||
@ -3852,18 +3925,20 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||||||
if_chain! {
|
if_chain! {
|
||||||
if let TraitItemKind::Fn(ref sig, _) = item.kind;
|
if let TraitItemKind::Fn(ref sig, _) = item.kind;
|
||||||
if sig.decl.implicit_self.has_implicit_self();
|
if sig.decl.implicit_self.has_implicit_self();
|
||||||
if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
|
if let Some(first_arg_hir_ty) = sig.decl.inputs.first();
|
||||||
|
if let Some(&first_arg_ty) = cx.tcx.fn_sig(item.owner_id)
|
||||||
|
.instantiate_identity()
|
||||||
|
.inputs()
|
||||||
|
.skip_binder()
|
||||||
|
.first();
|
||||||
then {
|
then {
|
||||||
let first_arg_span = first_arg_ty.span;
|
|
||||||
let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
|
|
||||||
let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty();
|
let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty();
|
||||||
wrong_self_convention::check(
|
wrong_self_convention::check(
|
||||||
cx,
|
cx,
|
||||||
item.ident.name.as_str(),
|
item.ident.name.as_str(),
|
||||||
self_ty,
|
self_ty,
|
||||||
first_arg_ty,
|
first_arg_ty,
|
||||||
first_arg_span,
|
first_arg_hir_ty.span,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
@ -3929,6 +4004,7 @@ impl Methods {
|
|||||||
("as_deref" | "as_deref_mut", []) => {
|
("as_deref" | "as_deref_mut", []) => {
|
||||||
needless_option_as_deref::check(cx, expr, recv, name);
|
needless_option_as_deref::check(cx, expr, recv, name);
|
||||||
},
|
},
|
||||||
|
("as_bytes" | "is_empty", []) => if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); },
|
||||||
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
|
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
|
||||||
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
||||||
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
|
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
|
||||||
@ -3978,6 +4054,7 @@ impl Methods {
|
|||||||
if let ExprKind::MethodCall(.., span) = expr.kind {
|
if let ExprKind::MethodCall(.., span) = expr.kind {
|
||||||
case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
|
case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
|
||||||
}
|
}
|
||||||
|
path_ends_with_ext::check(cx, recv, arg, expr, &self.msrv, &self.allowed_dotfiles);
|
||||||
},
|
},
|
||||||
("expect", [_]) => {
|
("expect", [_]) => {
|
||||||
match method_call(recv) {
|
match method_call(recv) {
|
||||||
|
53
clippy_lints/src/methods/path_ends_with_ext.rs
Normal file
53
clippy_lints/src/methods/path_ends_with_ext.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use super::PATH_ENDS_WITH_EXT;
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::msrvs;
|
||||||
|
use clippy_utils::msrvs::Msrv;
|
||||||
|
use clippy_utils::source::snippet;
|
||||||
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
|
use rustc_ast::{LitKind, StrStyle};
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::{Expr, ExprKind};
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_span::sym;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
pub const DEFAULT_ALLOWED_DOTFILES: &[&str] = &[
|
||||||
|
"git", "svn", "gem", "npm", "vim", "env", "rnd", "ssh", "vnc", "smb", "nvm", "bin",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub(super) fn check(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
recv: &Expr<'_>,
|
||||||
|
path: &Expr<'_>,
|
||||||
|
expr: &Expr<'_>,
|
||||||
|
msrv: &Msrv,
|
||||||
|
allowed_dotfiles: &FxHashSet<String>,
|
||||||
|
) {
|
||||||
|
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::Path)
|
||||||
|
&& !path.span.from_expansion()
|
||||||
|
&& let ExprKind::Lit(lit) = path.kind
|
||||||
|
&& let LitKind::Str(path, StrStyle::Cooked) = lit.node
|
||||||
|
&& let Some(path) = path.as_str().strip_prefix('.')
|
||||||
|
&& (1..=3).contains(&path.len())
|
||||||
|
&& !allowed_dotfiles.contains(path)
|
||||||
|
&& path.chars().all(char::is_alphanumeric)
|
||||||
|
{
|
||||||
|
let mut sugg = snippet(cx, recv.span, "..").into_owned();
|
||||||
|
if msrv.meets(msrvs::OPTION_IS_SOME_AND) {
|
||||||
|
let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#);
|
||||||
|
} else {
|
||||||
|
let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#);
|
||||||
|
};
|
||||||
|
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
PATH_ENDS_WITH_EXT,
|
||||||
|
expr.span,
|
||||||
|
"this looks like a failed attempt at checking for the file extension",
|
||||||
|
"try",
|
||||||
|
sugg,
|
||||||
|
Applicability::MaybeIncorrect
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
34
clippy_lints/src/methods/redundant_as_str.rs
Normal file
34
clippy_lints/src/methods/redundant_as_str.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use super::REDUNDANT_AS_STR;
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::Expr;
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_middle::query::Key;
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
pub(super) fn check(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
_expr: &Expr<'_>,
|
||||||
|
recv: &Expr<'_>,
|
||||||
|
as_str_span: Span,
|
||||||
|
other_method_span: Span,
|
||||||
|
) {
|
||||||
|
if cx
|
||||||
|
.tcx
|
||||||
|
.lang_items()
|
||||||
|
.string()
|
||||||
|
.is_some_and(|id| Some(id) == cx.typeck_results().expr_ty(recv).ty_adt_id())
|
||||||
|
{
|
||||||
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
REDUNDANT_AS_STR,
|
||||||
|
as_str_span.to(other_method_span),
|
||||||
|
"this `as_str` is redundant and can be removed as the method immediately following exists on `String` too",
|
||||||
|
"try",
|
||||||
|
snippet_with_applicability(cx, other_method_span, "..", &mut applicability).into_owned(),
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,8 @@ use if_chain::if_chain;
|
|||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty::{self, GenericArgKind};
|
use rustc_middle::ty;
|
||||||
|
use rustc_middle::ty::GenericArgKind;
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
@ -1,24 +1,22 @@
|
|||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then};
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_then, span_lint_hir_and_then};
|
||||||
use clippy_utils::source::{snippet, snippet_opt, snippet_with_context};
|
use clippy_utils::source::{snippet, snippet_with_context};
|
||||||
use if_chain::if_chain;
|
|
||||||
use rustc_errors::Applicability;
|
|
||||||
use rustc_hir::intravisit::FnKind;
|
|
||||||
use rustc_hir::{
|
|
||||||
self as hir, def, BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, Stmt,
|
|
||||||
StmtKind, TyKind,
|
|
||||||
};
|
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
|
||||||
use rustc_middle::lint::in_external_macro;
|
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
|
||||||
use rustc_span::def_id::LocalDefId;
|
|
||||||
use rustc_span::hygiene::DesugaringKind;
|
|
||||||
use rustc_span::source_map::{ExpnKind, Span};
|
|
||||||
|
|
||||||
use clippy_utils::sugg::Sugg;
|
use clippy_utils::sugg::Sugg;
|
||||||
use clippy_utils::{
|
use clippy_utils::{
|
||||||
get_parent_expr, in_constant, is_integer_literal, is_lint_allowed, is_no_std_crate, iter_input_pats,
|
any_parent_is_automatically_derived, fulfill_or_allowed, get_parent_expr, is_lint_allowed, iter_input_pats,
|
||||||
last_path_segment, SpanlessEq,
|
last_path_segment, SpanlessEq,
|
||||||
};
|
};
|
||||||
|
use if_chain::if_chain;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::def::Res;
|
||||||
|
use rustc_hir::intravisit::FnKind;
|
||||||
|
use rustc_hir::{
|
||||||
|
BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind,
|
||||||
|
};
|
||||||
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
|
use rustc_middle::lint::in_external_macro;
|
||||||
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
|
use rustc_span::def_id::LocalDefId;
|
||||||
|
use rustc_span::source_map::Span;
|
||||||
|
|
||||||
use crate::ref_patterns::REF_PATTERNS;
|
use crate::ref_patterns::REF_PATTERNS;
|
||||||
|
|
||||||
@ -56,6 +54,7 @@ declare_clippy_lint! {
|
|||||||
style,
|
style,
|
||||||
"an entire binding declared as `ref`, in a function argument or a `let` statement"
|
"an entire binding declared as `ref`, in a function argument or a `let` statement"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks for the use of bindings with a single leading
|
/// Checks for the use of bindings with a single leading
|
||||||
@ -103,51 +102,13 @@ declare_clippy_lint! {
|
|||||||
"using a short circuit boolean condition as a statement"
|
"using a short circuit boolean condition as a statement"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_lint_pass!(LintPass => [
|
||||||
/// ### What it does
|
|
||||||
/// Catch casts from `0` to some pointer type
|
|
||||||
///
|
|
||||||
/// ### Why is this bad?
|
|
||||||
/// This generally means `null` and is better expressed as
|
|
||||||
/// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
|
|
||||||
///
|
|
||||||
/// ### Example
|
|
||||||
/// ```rust
|
|
||||||
/// let a = 0 as *const u32;
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Use instead:
|
|
||||||
/// ```rust
|
|
||||||
/// let a = std::ptr::null::<u32>();
|
|
||||||
/// ```
|
|
||||||
#[clippy::version = "pre 1.29.0"]
|
|
||||||
pub ZERO_PTR,
|
|
||||||
style,
|
|
||||||
"using `0 as *{const, mut} T`"
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LintPass {
|
|
||||||
std_or_core: &'static str,
|
|
||||||
}
|
|
||||||
impl Default for LintPass {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { std_or_core: "std" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl_lint_pass!(LintPass => [
|
|
||||||
TOPLEVEL_REF_ARG,
|
TOPLEVEL_REF_ARG,
|
||||||
USED_UNDERSCORE_BINDING,
|
USED_UNDERSCORE_BINDING,
|
||||||
SHORT_CIRCUIT_STATEMENT,
|
SHORT_CIRCUIT_STATEMENT,
|
||||||
ZERO_PTR,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for LintPass {
|
impl<'tcx> LateLintPass<'tcx> for LintPass {
|
||||||
fn check_crate(&mut self, cx: &LateContext<'_>) {
|
|
||||||
if is_no_std_crate(cx) {
|
|
||||||
self.std_or_core = "core";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_fn(
|
fn check_fn(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
@ -253,50 +214,56 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
if let ExprKind::Cast(e, ty) = expr.kind {
|
if in_external_macro(cx.sess(), expr.span)
|
||||||
self.check_cast(cx, expr.span, e, ty);
|
|| expr.span.desugaring_kind().is_some()
|
||||||
|
|| any_parent_is_automatically_derived(cx.tcx, expr.hir_id)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) {
|
let (definition_hir_id, ident) = match expr.kind {
|
||||||
// Don't lint things expanded by #[derive(...)], etc or `await` desugaring
|
ExprKind::Path(ref qpath) => {
|
||||||
return;
|
if let QPath::Resolved(None, path) = qpath
|
||||||
}
|
&& let Res::Local(id) = path.res
|
||||||
let sym;
|
&& is_used(cx, expr)
|
||||||
let binding = match expr.kind {
|
|
||||||
ExprKind::Path(ref qpath) if !matches!(qpath, hir::QPath::LangItem(..)) => {
|
|
||||||
let binding = last_path_segment(qpath).ident.as_str();
|
|
||||||
if binding.starts_with('_') &&
|
|
||||||
!binding.starts_with("__") &&
|
|
||||||
binding != "_result" && // FIXME: #944
|
|
||||||
is_used(cx, expr) &&
|
|
||||||
// don't lint if the declaration is in a macro
|
|
||||||
non_macro_local(cx, cx.qpath_res(qpath, expr.hir_id))
|
|
||||||
{
|
{
|
||||||
Some(binding)
|
(id, last_path_segment(qpath).ident)
|
||||||
} else {
|
} else {
|
||||||
None
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ExprKind::Field(_, ident) => {
|
ExprKind::Field(recv, ident) => {
|
||||||
sym = ident.name;
|
if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def()
|
||||||
let name = sym.as_str();
|
&& let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name)
|
||||||
if name.starts_with('_') && !name.starts_with("__") {
|
&& let Some(local_did) = field.did.as_local()
|
||||||
Some(name)
|
&& let Some(hir_id) = cx.tcx.opt_local_def_id_to_hir_id(local_did)
|
||||||
|
&& !cx.tcx.type_of(field.did).skip_binder().is_phantom_data()
|
||||||
|
{
|
||||||
|
(hir_id, ident)
|
||||||
} else {
|
} else {
|
||||||
None
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => return,
|
||||||
};
|
};
|
||||||
if let Some(binding) = binding {
|
|
||||||
span_lint(
|
let name = ident.name.as_str();
|
||||||
|
if name.starts_with('_')
|
||||||
|
&& !name.starts_with("__")
|
||||||
|
&& let definition_span = cx.tcx.hir().span(definition_hir_id)
|
||||||
|
&& !definition_span.from_expansion()
|
||||||
|
&& !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id])
|
||||||
|
{
|
||||||
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
USED_UNDERSCORE_BINDING,
|
USED_UNDERSCORE_BINDING,
|
||||||
expr.span,
|
expr.span,
|
||||||
&format!(
|
&format!(
|
||||||
"used binding `{binding}` which is prefixed with an underscore. A leading \
|
"used binding `{name}` which is prefixed with an underscore. A leading \
|
||||||
underscore signals that a binding will not be used"
|
underscore signals that a binding will not be used"
|
||||||
),
|
),
|
||||||
|
|diag| {
|
||||||
|
diag.span_note(definition_span, format!("`{name}` is defined here"));
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -311,50 +278,3 @@ fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||||||
_ => is_used(cx, parent),
|
_ => is_used(cx, parent),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests whether an expression is in a macro expansion (e.g., something
|
|
||||||
/// generated by `#[derive(...)]` or the like).
|
|
||||||
fn in_attributes_expansion(expr: &Expr<'_>) -> bool {
|
|
||||||
use rustc_span::hygiene::MacroKind;
|
|
||||||
if expr.span.from_expansion() {
|
|
||||||
let data = expr.span.ctxt().outer_expn_data();
|
|
||||||
matches!(data.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, _))
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests whether `res` is a variable defined outside a macro.
|
|
||||||
fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool {
|
|
||||||
if let def::Res::Local(id) = res {
|
|
||||||
!cx.tcx.hir().span(id).from_expansion()
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LintPass {
|
|
||||||
fn check_cast(&self, cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) {
|
|
||||||
if_chain! {
|
|
||||||
if let TyKind::Ptr(ref mut_ty) = ty.kind;
|
|
||||||
if is_integer_literal(e, 0);
|
|
||||||
if !in_constant(cx, e.hir_id);
|
|
||||||
then {
|
|
||||||
let (msg, sugg_fn) = match mut_ty.mutbl {
|
|
||||||
Mutability::Mut => ("`0 as *mut _` detected", "ptr::null_mut"),
|
|
||||||
Mutability::Not => ("`0 as *const _` detected", "ptr::null"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind {
|
|
||||||
(format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MachineApplicable)
|
|
||||||
} else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) {
|
|
||||||
(format!("{}::{sugg_fn}::<{mut_ty_snip}>()", self.std_or_core), Applicability::MachineApplicable)
|
|
||||||
} else {
|
|
||||||
// `MaybeIncorrect` as type inference may not work with the suggested code
|
|
||||||
(format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MaybeIncorrect)
|
|
||||||
};
|
|
||||||
span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -7,7 +7,6 @@ use rustc_hir as hir;
|
|||||||
use rustc_hir::def_id::CRATE_DEF_ID;
|
use rustc_hir::def_id::CRATE_DEF_ID;
|
||||||
use rustc_hir::intravisit::FnKind;
|
use rustc_hir::intravisit::FnKind;
|
||||||
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind};
|
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind};
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
@ -124,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
|||||||
FnKind::Method(_, sig, ..) => {
|
FnKind::Method(_, sig, ..) => {
|
||||||
if trait_ref_of_method(cx, def_id).is_some()
|
if trait_ref_of_method(cx, def_id).is_some()
|
||||||
|| already_const(sig.header)
|
|| already_const(sig.header)
|
||||||
|| method_accepts_droppable(cx, sig.decl.inputs)
|
|| method_accepts_droppable(cx, def_id)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -165,12 +164,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
|||||||
|
|
||||||
/// Returns true if any of the method parameters is a type that implements `Drop`. The method
|
/// Returns true if any of the method parameters is a type that implements `Drop`. The method
|
||||||
/// can't be made const then, because `drop` can't be const-evaluated.
|
/// can't be made const then, because `drop` can't be const-evaluated.
|
||||||
fn method_accepts_droppable(cx: &LateContext<'_>, param_tys: &[hir::Ty<'_>]) -> bool {
|
fn method_accepts_droppable(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
|
||||||
|
let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
|
||||||
|
|
||||||
// If any of the params are droppable, return true
|
// If any of the params are droppable, return true
|
||||||
param_tys.iter().any(|hir_ty| {
|
sig.inputs().iter().any(|&ty| has_drop(cx, ty))
|
||||||
let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
|
||||||
has_drop(cx, ty_ty)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't have to lint on something that's already `const`
|
// We don't have to lint on something that's already `const`
|
||||||
|
@ -96,10 +96,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
|
|||||||
self.found = true;
|
self.found = true;
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
ExprKind::If(..) => {
|
|
||||||
self.found = true;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
ExprKind::Path(_) => {
|
ExprKind::Path(_) => {
|
||||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||||
if adj
|
if adj
|
||||||
|
410
clippy_lints/src/needless_borrows_for_generic_args.rs
Normal file
410
clippy_lints/src/needless_borrows_for_generic_args.rs
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
|
use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
|
use clippy_utils::source::snippet_with_context;
|
||||||
|
use clippy_utils::ty::is_copy;
|
||||||
|
use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode};
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::def::{DefKind, Res};
|
||||||
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
|
use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath};
|
||||||
|
use rustc_index::bit_set::BitSet;
|
||||||
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
use rustc_middle::mir::{Rvalue, StatementKind};
|
||||||
|
use rustc_middle::ty::{
|
||||||
|
self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamTy, ProjectionPredicate, Ty,
|
||||||
|
};
|
||||||
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
|
use rustc_span::symbol::sym;
|
||||||
|
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||||
|
use rustc_trait_selection::traits::{Obligation, ObligationCause};
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for borrow operations (`&`) that used as a generic argument to a
|
||||||
|
/// function when the borrowed value could be used.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// Suggests that the receiver of the expression borrows
|
||||||
|
/// the expression.
|
||||||
|
///
|
||||||
|
/// ### Known problems
|
||||||
|
/// The lint cannot tell when the implementation of a trait
|
||||||
|
/// for `&T` and `T` do different things. Removing a borrow
|
||||||
|
/// in such a case can change the semantics of the code.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// fn f(_: impl AsRef<str>) {}
|
||||||
|
///
|
||||||
|
/// let x = "foo";
|
||||||
|
/// f(&x);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// fn f(_: impl AsRef<str>) {}
|
||||||
|
///
|
||||||
|
/// let x = "foo";
|
||||||
|
/// f(x);
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "pre 1.29.0"]
|
||||||
|
pub NEEDLESS_BORROWS_FOR_GENERIC_ARGS,
|
||||||
|
style,
|
||||||
|
"taking a reference that is going to be automatically dereferenced"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NeedlessBorrowsForGenericArgs<'tcx> {
|
||||||
|
/// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by
|
||||||
|
/// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead
|
||||||
|
/// be moved.
|
||||||
|
possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||||
|
|
||||||
|
// `IntoIterator` for arrays requires Rust 1.53.
|
||||||
|
msrv: Msrv,
|
||||||
|
}
|
||||||
|
impl_lint_pass!(NeedlessBorrowsForGenericArgs<'_> => [NEEDLESS_BORROWS_FOR_GENERIC_ARGS]);
|
||||||
|
|
||||||
|
impl NeedlessBorrowsForGenericArgs<'_> {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
|
Self {
|
||||||
|
possible_borrowers: Vec::new(),
|
||||||
|
msrv,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
|
||||||
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
|
if matches!(expr.kind, ExprKind::AddrOf(..))
|
||||||
|
&& !expr.span.from_expansion()
|
||||||
|
&& let Some(use_cx) = expr_use_ctxt(cx, expr)
|
||||||
|
&& !use_cx.is_ty_unified
|
||||||
|
&& let Some(DefinedTy::Mir(ty)) = use_cx.node.defined_ty(cx)
|
||||||
|
&& let ty::Param(ty) = *ty.value.skip_binder().kind()
|
||||||
|
&& let Some((hir_id, fn_id, i)) = match use_cx.node {
|
||||||
|
ExprUseNode::MethodArg(_, _, 0) => None,
|
||||||
|
ExprUseNode::MethodArg(hir_id, None, i) => {
|
||||||
|
cx.typeck_results().type_dependent_def_id(hir_id).map(|id| (hir_id, id, i))
|
||||||
|
},
|
||||||
|
ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i)
|
||||||
|
if !path_has_args(p) => match cx.typeck_results().qpath_res(p, hir_id) {
|
||||||
|
Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => {
|
||||||
|
Some((hir_id, id, i))
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
} && let count = needless_borrow_count(
|
||||||
|
cx,
|
||||||
|
&mut self.possible_borrowers,
|
||||||
|
fn_id,
|
||||||
|
cx.typeck_results().node_args(hir_id),
|
||||||
|
i,
|
||||||
|
ty,
|
||||||
|
expr,
|
||||||
|
&self.msrv,
|
||||||
|
) && count != 0
|
||||||
|
{
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
NEEDLESS_BORROWS_FOR_GENERIC_ARGS,
|
||||||
|
expr.span,
|
||||||
|
"the borrowed expression implements the required traits",
|
||||||
|
|diag| {
|
||||||
|
let mut app = Applicability::MachineApplicable;
|
||||||
|
let snip_span = peel_n_hir_expr_refs(expr, count).0.span;
|
||||||
|
let snip = snippet_with_context(cx, snip_span, expr.span.ctxt(), "..", &mut app).0;
|
||||||
|
diag.span_suggestion(expr.span, "change this to", snip.into_owned(), app);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
|
||||||
|
if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| {
|
||||||
|
local_def_id == cx.tcx.hir().body_owner_def_id(body.id())
|
||||||
|
}) {
|
||||||
|
self.possible_borrowers.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_msrv_attr!(LateContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_has_args(p: &QPath<'_>) -> bool {
|
||||||
|
match *p {
|
||||||
|
QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks for the number of borrow expressions which can be removed from the given expression
|
||||||
|
/// where the expression is used as an argument to a function expecting a generic type.
|
||||||
|
///
|
||||||
|
/// The following constraints will be checked:
|
||||||
|
/// * The borrowed expression meets all the generic type's constraints.
|
||||||
|
/// * The generic type appears only once in the functions signature.
|
||||||
|
/// * The borrowed value will not be moved if it is used later in the function.
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
fn needless_borrow_count<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||||
|
fn_id: DefId,
|
||||||
|
callee_args: &'tcx List<GenericArg<'tcx>>,
|
||||||
|
arg_index: usize,
|
||||||
|
param_ty: ParamTy,
|
||||||
|
mut expr: &Expr<'tcx>,
|
||||||
|
msrv: &Msrv,
|
||||||
|
) -> usize {
|
||||||
|
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
|
||||||
|
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
|
||||||
|
|
||||||
|
let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder();
|
||||||
|
let predicates = cx.tcx.param_env(fn_id).caller_bounds();
|
||||||
|
let projection_predicates = predicates
|
||||||
|
.iter()
|
||||||
|
.filter_map(|predicate| {
|
||||||
|
if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
|
||||||
|
Some(projection_predicate)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut trait_with_ref_mut_self_method = false;
|
||||||
|
|
||||||
|
// If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
|
||||||
|
if predicates
|
||||||
|
.iter()
|
||||||
|
.filter_map(|predicate| {
|
||||||
|
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||||
|
&& trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
|
||||||
|
{
|
||||||
|
Some(trait_predicate.trait_ref.def_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.inspect(|trait_def_id| {
|
||||||
|
trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
|
||||||
|
})
|
||||||
|
.all(|trait_def_id| {
|
||||||
|
Some(trait_def_id) == destruct_trait_def_id
|
||||||
|
|| Some(trait_def_id) == sized_trait_def_id
|
||||||
|
|| cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See:
|
||||||
|
// - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201
|
||||||
|
// - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232
|
||||||
|
if projection_predicates
|
||||||
|
.iter()
|
||||||
|
.any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// `args_with_referent_ty` can be constructed outside of `check_referent` because the same
|
||||||
|
// elements are modified each time `check_referent` is called.
|
||||||
|
let mut args_with_referent_ty = callee_args.to_vec();
|
||||||
|
|
||||||
|
let mut check_reference_and_referent = |reference, referent| {
|
||||||
|
let referent_ty = cx.typeck_results().expr_ty(referent);
|
||||||
|
|
||||||
|
if !is_copy(cx, referent_ty)
|
||||||
|
&& (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
|
||||||
|
|| !referent_used_exactly_once(cx, possible_borrowers, reference))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
|
||||||
|
if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !replace_types(
|
||||||
|
cx,
|
||||||
|
param_ty,
|
||||||
|
referent_ty,
|
||||||
|
fn_sig,
|
||||||
|
arg_index,
|
||||||
|
&projection_predicates,
|
||||||
|
&mut args_with_referent_ty,
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
predicates.iter().all(|predicate| {
|
||||||
|
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||||
|
&& cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
|
||||||
|
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
|
||||||
|
&& let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack()
|
||||||
|
&& ty.is_array()
|
||||||
|
&& !msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty);
|
||||||
|
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
|
||||||
|
let infcx = cx.tcx.infer_ctxt().build();
|
||||||
|
infcx.predicate_must_hold_modulo_regions(&obligation)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
while let ExprKind::AddrOf(_, _, referent) = expr.kind {
|
||||||
|
if !check_reference_and_referent(expr, referent) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
expr = referent;
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
count
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
|
||||||
|
cx.tcx
|
||||||
|
.associated_items(trait_def_id)
|
||||||
|
.in_definition_order()
|
||||||
|
.any(|assoc_item| {
|
||||||
|
if assoc_item.fn_has_self_parameter {
|
||||||
|
let self_ty = cx
|
||||||
|
.tcx
|
||||||
|
.fn_sig(assoc_item.def_id)
|
||||||
|
.instantiate_identity()
|
||||||
|
.skip_binder()
|
||||||
|
.inputs()[0];
|
||||||
|
matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_mixed_projection_predicate<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
callee_def_id: DefId,
|
||||||
|
projection_predicate: &ProjectionPredicate<'tcx>,
|
||||||
|
) -> bool {
|
||||||
|
let generics = cx.tcx.generics_of(callee_def_id);
|
||||||
|
// The predicate requires the projected type to equal a type parameter from the parent context.
|
||||||
|
if let Some(term_ty) = projection_predicate.term.ty()
|
||||||
|
&& let ty::Param(term_param_ty) = term_ty.kind()
|
||||||
|
&& (term_param_ty.index as usize) < generics.parent_count
|
||||||
|
{
|
||||||
|
// The inner-most self type is a type parameter from the current function.
|
||||||
|
let mut projection_ty = projection_predicate.projection_ty;
|
||||||
|
loop {
|
||||||
|
match projection_ty.self_ty().kind() {
|
||||||
|
ty::Alias(ty::Projection, inner_projection_ty) => {
|
||||||
|
projection_ty = *inner_projection_ty;
|
||||||
|
}
|
||||||
|
ty::Param(param_ty) => {
|
||||||
|
return (param_ty.index as usize) >= generics.parent_count;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn referent_used_exactly_once<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
|
||||||
|
reference: &Expr<'tcx>,
|
||||||
|
) -> bool {
|
||||||
|
if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id)
|
||||||
|
&& let Some(local) = expr_local(cx.tcx, reference)
|
||||||
|
&& let [location] = *local_assignments(mir, local).as_slice()
|
||||||
|
&& let block_data = &mir.basic_blocks[location.block]
|
||||||
|
&& let Some(statement) = block_data.statements.get(location.statement_index)
|
||||||
|
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
|
||||||
|
&& !place.is_indirect_first_projection()
|
||||||
|
{
|
||||||
|
let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
|
||||||
|
if possible_borrowers
|
||||||
|
.last()
|
||||||
|
.map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
|
||||||
|
{
|
||||||
|
possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
|
||||||
|
}
|
||||||
|
let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
|
||||||
|
// If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
|
||||||
|
// that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
|
||||||
|
// itself. See the comment in that method for an explanation as to why.
|
||||||
|
possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
|
||||||
|
&& used_exactly_once(mir, place.local).unwrap_or(false)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
|
||||||
|
// projected type that is a type parameter. Returns `false` if replacing the types would have an
|
||||||
|
// effect on the function signature beyond substituting `new_ty` for `param_ty`.
|
||||||
|
// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
|
||||||
|
fn replace_types<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
param_ty: ParamTy,
|
||||||
|
new_ty: Ty<'tcx>,
|
||||||
|
fn_sig: FnSig<'tcx>,
|
||||||
|
arg_index: usize,
|
||||||
|
projection_predicates: &[ProjectionPredicate<'tcx>],
|
||||||
|
args: &mut [ty::GenericArg<'tcx>],
|
||||||
|
) -> bool {
|
||||||
|
let mut replaced = BitSet::new_empty(args.len());
|
||||||
|
|
||||||
|
let mut deque = VecDeque::with_capacity(args.len());
|
||||||
|
deque.push_back((param_ty, new_ty));
|
||||||
|
|
||||||
|
while let Some((param_ty, new_ty)) = deque.pop_front() {
|
||||||
|
// If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
|
||||||
|
if !fn_sig
|
||||||
|
.inputs_and_output
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
args[param_ty.index as usize] = ty::GenericArg::from(new_ty);
|
||||||
|
|
||||||
|
// The `replaced.insert(...)` check provides some protection against infinite loops.
|
||||||
|
if replaced.insert(param_ty.index) {
|
||||||
|
for projection_predicate in projection_predicates {
|
||||||
|
if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
|
||||||
|
&& let Some(term_ty) = projection_predicate.term.ty()
|
||||||
|
&& let ty::Param(term_param_ty) = term_ty.kind()
|
||||||
|
{
|
||||||
|
let projection = cx.tcx.mk_ty_from_kind(ty::Alias(
|
||||||
|
ty::Projection,
|
||||||
|
projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty),
|
||||||
|
));
|
||||||
|
|
||||||
|
if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
|
||||||
|
&& args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
|
||||||
|
{
|
||||||
|
deque.push_back((*term_param_ty, projected_ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
use super::needless_pass_by_value::requires_exact_signature;
|
use super::needless_pass_by_value::requires_exact_signature;
|
||||||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
|
use clippy_utils::visitors::for_each_expr_with_closures;
|
||||||
use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self};
|
use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self};
|
||||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
@ -9,7 +10,7 @@ use rustc_hir::{
|
|||||||
Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath,
|
Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath,
|
||||||
};
|
};
|
||||||
use rustc_hir_typeck::expr_use_visitor as euv;
|
use rustc_hir_typeck::expr_use_visitor as euv;
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::hir::map::associated_body;
|
use rustc_middle::hir::map::associated_body;
|
||||||
use rustc_middle::hir::nested_filter::OnlyBodies;
|
use rustc_middle::hir::nested_filter::OnlyBodies;
|
||||||
@ -21,6 +22,8 @@ use rustc_span::symbol::kw;
|
|||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
|
|
||||||
|
use core::ops::ControlFlow;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Check if a `&mut` function argument is actually used mutably.
|
/// Check if a `&mut` function argument is actually used mutably.
|
||||||
@ -95,6 +98,30 @@ fn should_skip<'tcx>(
|
|||||||
is_from_proc_macro(cx, &input)
|
is_from_proc_macro(cx, &input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_closures<'tcx>(
|
||||||
|
ctx: &mut MutablyUsedVariablesCtxt<'tcx>,
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
infcx: &InferCtxt<'tcx>,
|
||||||
|
checked_closures: &mut FxHashSet<LocalDefId>,
|
||||||
|
closures: FxHashSet<LocalDefId>,
|
||||||
|
) {
|
||||||
|
let hir = cx.tcx.hir();
|
||||||
|
for closure in closures {
|
||||||
|
if !checked_closures.insert(closure) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ctx.prev_bind = None;
|
||||||
|
ctx.prev_move_to_closure.clear();
|
||||||
|
if let Some(body) = hir
|
||||||
|
.find_by_def_id(closure)
|
||||||
|
.and_then(associated_body)
|
||||||
|
.map(|(_, body_id)| hir.body(body_id))
|
||||||
|
{
|
||||||
|
euv::ExprUseVisitor::new(ctx, infcx, closure, cx.param_env, cx.typeck_results()).consume_body(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
|
impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
|
||||||
fn check_fn(
|
fn check_fn(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -161,25 +188,22 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
|
|||||||
euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
|
euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
|
||||||
if is_async {
|
if is_async {
|
||||||
let mut checked_closures = FxHashSet::default();
|
let mut checked_closures = FxHashSet::default();
|
||||||
while !ctx.async_closures.is_empty() {
|
|
||||||
let closures = ctx.async_closures.clone();
|
// We retrieve all the closures declared in the async function because they will
|
||||||
ctx.async_closures.clear();
|
// not be found by `euv::Delegate`.
|
||||||
let hir = cx.tcx.hir();
|
let mut closures: FxHashSet<LocalDefId> = FxHashSet::default();
|
||||||
for closure in closures {
|
for_each_expr_with_closures(cx, body, |expr| {
|
||||||
if !checked_closures.insert(closure) {
|
if let ExprKind::Closure(closure) = expr.kind {
|
||||||
continue;
|
closures.insert(closure.def_id);
|
||||||
}
|
|
||||||
ctx.prev_bind = None;
|
|
||||||
ctx.prev_move_to_closure.clear();
|
|
||||||
if let Some(body) = hir
|
|
||||||
.find_by_def_id(closure)
|
|
||||||
.and_then(associated_body)
|
|
||||||
.map(|(_, body_id)| hir.body(body_id))
|
|
||||||
{
|
|
||||||
euv::ExprUseVisitor::new(&mut ctx, &infcx, closure, cx.param_env, cx.typeck_results())
|
|
||||||
.consume_body(body);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
ControlFlow::<()>::Continue(())
|
||||||
|
});
|
||||||
|
check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures);
|
||||||
|
|
||||||
|
while !ctx.async_closures.is_empty() {
|
||||||
|
let async_closures = ctx.async_closures.clone();
|
||||||
|
ctx.async_closures.clear();
|
||||||
|
check_closures(&mut ctx, cx, &infcx, &mut checked_closures, async_closures);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx
|
ctx
|
||||||
@ -244,6 +268,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
|
|||||||
struct MutablyUsedVariablesCtxt<'tcx> {
|
struct MutablyUsedVariablesCtxt<'tcx> {
|
||||||
mutably_used_vars: HirIdSet,
|
mutably_used_vars: HirIdSet,
|
||||||
prev_bind: Option<HirId>,
|
prev_bind: Option<HirId>,
|
||||||
|
/// In async functions, the inner AST is composed of multiple layers until we reach the code
|
||||||
|
/// defined by the user. Because of that, some variables are marked as mutably borrowed even
|
||||||
|
/// though they're not. This field lists the `HirId` that should not be considered as mutable
|
||||||
|
/// use of a variable.
|
||||||
prev_move_to_closure: HirIdSet,
|
prev_move_to_closure: HirIdSet,
|
||||||
aliases: HirIdMap<HirId>,
|
aliases: HirIdMap<HirId>,
|
||||||
async_closures: FxHashSet<LocalDefId>,
|
async_closures: FxHashSet<LocalDefId>,
|
||||||
@ -308,7 +336,12 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
|
|||||||
fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId, borrow: ty::BorrowKind) {
|
fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId, borrow: ty::BorrowKind) {
|
||||||
self.prev_bind = None;
|
self.prev_bind = None;
|
||||||
if let euv::Place {
|
if let euv::Place {
|
||||||
base: euv::PlaceBase::Local(vid),
|
base:
|
||||||
|
euv::PlaceBase::Local(vid)
|
||||||
|
| euv::PlaceBase::Upvar(UpvarId {
|
||||||
|
var_path: UpvarPath { hir_id: vid },
|
||||||
|
..
|
||||||
|
}),
|
||||||
base_ty,
|
base_ty,
|
||||||
..
|
..
|
||||||
} = &cmt.place
|
} = &cmt.place
|
||||||
|
@ -5,10 +5,8 @@ use clippy_utils::{get_parent_node, is_lint_allowed, peel_blocks};
|
|||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, FnRetTy, ItemKind, Node, PatKind, Stmt, StmtKind,
|
is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, ItemKind, Node, PatKind, Stmt, StmtKind, UnsafeSource,
|
||||||
UnsafeSource,
|
|
||||||
};
|
};
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
|
||||||
use rustc_infer::infer::TyCtxtInferExt as _;
|
use rustc_infer::infer::TyCtxtInferExt as _;
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
@ -99,14 +97,13 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
|||||||
|diag| {
|
|diag| {
|
||||||
for parent in cx.tcx.hir().parent_iter(stmt.hir_id) {
|
for parent in cx.tcx.hir().parent_iter(stmt.hir_id) {
|
||||||
if let Node::Item(item) = parent.1
|
if let Node::Item(item) = parent.1
|
||||||
&& let ItemKind::Fn(sig, ..) = item.kind
|
&& let ItemKind::Fn(..) = item.kind
|
||||||
&& let FnRetTy::Return(ret_ty) = sig.decl.output
|
|
||||||
&& let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id)
|
&& let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id)
|
||||||
&& let [.., final_stmt] = block.stmts
|
&& let [.., final_stmt] = block.stmts
|
||||||
&& final_stmt.hir_id == stmt.hir_id
|
&& final_stmt.hir_id == stmt.hir_id
|
||||||
{
|
{
|
||||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||||
let mut ret_ty = hir_ty_to_ty(cx.tcx, ret_ty);
|
let mut ret_ty = cx.tcx.fn_sig(item.owner_id).instantiate_identity().output().skip_binder();
|
||||||
|
|
||||||
// Remove `impl Future<Output = T>` to get `T`
|
// Remove `impl Future<Output = T>` to get `T`
|
||||||
if cx.tcx.ty_is_opaque_future(ret_ty) &&
|
if cx.tcx.ty_is_opaque_future(ret_ty) &&
|
||||||
@ -115,7 +112,7 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
|||||||
ret_ty = true_ret_ty;
|
ret_ty = true_ret_ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ret_ty == expr_ty {
|
if !ret_ty.is_unit() && ret_ty == expr_ty {
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(
|
||||||
stmt.span.shrink_to_lo(),
|
stmt.span.shrink_to_lo(),
|
||||||
"did you mean to return it?",
|
"did you mean to return it?",
|
||||||
|
@ -4,7 +4,7 @@ use clippy_utils::ty::implements_trait;
|
|||||||
use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, match_def_path, path_res, std_or_core};
|
use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, match_def_path, path_res, std_or_core};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, ItemKind, LangItem, Node, UnOp};
|
use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::EarlyBinder;
|
use rustc_middle::ty::EarlyBinder;
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
@ -122,9 +122,6 @@ impl LateLintPass<'_> for NonCanonicalImpls {
|
|||||||
if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) {
|
if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let ItemKind::Impl(_) = item.kind else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir().impl_item(impl_item.impl_item_id()).kind else {
|
let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir().impl_item(impl_item.impl_item_id()).kind else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -180,17 +177,8 @@ impl LateLintPass<'_> for NonCanonicalImpls {
|
|||||||
|
|
||||||
if cx.tcx.is_diagnostic_item(sym::PartialOrd, trait_impl.def_id)
|
if cx.tcx.is_diagnostic_item(sym::PartialOrd, trait_impl.def_id)
|
||||||
&& impl_item.ident.name == sym::partial_cmp
|
&& impl_item.ident.name == sym::partial_cmp
|
||||||
&& let Some(ord_def_id) = cx
|
&& let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord)
|
||||||
.tcx
|
&& implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[])
|
||||||
.diagnostic_items(trait_impl.def_id.krate)
|
|
||||||
.name_to_id
|
|
||||||
.get(&sym::Ord)
|
|
||||||
&& implements_trait(
|
|
||||||
cx,
|
|
||||||
trait_impl.self_ty(),
|
|
||||||
*ord_def_id,
|
|
||||||
&[],
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
// If the `cmp` call likely needs to be fully qualified in the suggestion
|
// If the `cmp` call likely needs to be fully qualified in the suggestion
|
||||||
// (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't
|
// (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't
|
||||||
|
@ -13,7 +13,6 @@ use rustc_hir::def_id::DefId;
|
|||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp,
|
BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp,
|
||||||
};
|
};
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
|
||||||
use rustc_lint::{LateContext, LateLintPass, Lint};
|
use rustc_lint::{LateContext, LateLintPass, Lint};
|
||||||
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId};
|
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId};
|
||||||
use rustc_middle::ty::adjustment::Adjust;
|
use rustc_middle::ty::adjustment::Adjust;
|
||||||
@ -297,8 +296,8 @@ declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTER
|
|||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
||||||
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
|
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
|
||||||
if let ItemKind::Const(hir_ty, _generics, body_id) = it.kind {
|
if let ItemKind::Const(.., body_id) = it.kind {
|
||||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
let ty = cx.tcx.type_of(it.owner_id).instantiate_identity();
|
||||||
if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) {
|
if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) {
|
||||||
lint(cx, Source::Item { item: it.span });
|
lint(cx, Source::Item { item: it.span });
|
||||||
}
|
}
|
||||||
@ -306,8 +305,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) {
|
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) {
|
||||||
if let TraitItemKind::Const(hir_ty, body_id_opt) = &trait_item.kind {
|
if let TraitItemKind::Const(_, body_id_opt) = &trait_item.kind {
|
||||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
let ty = cx.tcx.type_of(trait_item.owner_id).instantiate_identity();
|
||||||
|
|
||||||
// Normalize assoc types because ones originated from generic params
|
// Normalize assoc types because ones originated from generic params
|
||||||
// bounded other traits could have their bound.
|
// bounded other traits could have their bound.
|
||||||
@ -333,7 +332,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
|
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
|
||||||
if let ImplItemKind::Const(hir_ty, body_id) = &impl_item.kind {
|
if let ImplItemKind::Const(_, body_id) = &impl_item.kind {
|
||||||
let item_def_id = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
|
let item_def_id = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
|
||||||
let item = cx.tcx.hir().expect_item(item_def_id);
|
let item = cx.tcx.hir().expect_item(item_def_id);
|
||||||
|
|
||||||
@ -366,7 +365,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
|||||||
// we should use here as a frozen variant is a potential to be frozen
|
// we should use here as a frozen variant is a potential to be frozen
|
||||||
// similar to unknown layouts.
|
// similar to unknown layouts.
|
||||||
// e.g. `layout_of(...).is_err() || has_frozen_variant(...);`
|
// e.g. `layout_of(...).is_err() || has_frozen_variant(...);`
|
||||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity();
|
||||||
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
|
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
|
||||||
if is_unfrozen(cx, normalized);
|
if is_unfrozen(cx, normalized);
|
||||||
if is_value_unfrozen_poly(cx, *body_id, normalized);
|
if is_value_unfrozen_poly(cx, *body_id, normalized);
|
||||||
@ -381,7 +380,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
ItemKind::Impl(Impl { of_trait: None, .. }) => {
|
ItemKind::Impl(Impl { of_trait: None, .. }) => {
|
||||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity();
|
||||||
// Normalize assoc types originated from generic params.
|
// Normalize assoc types originated from generic params.
|
||||||
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
|
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ use rustc_hir::{
|
|||||||
ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind,
|
ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind,
|
||||||
TyKind, Unsafety,
|
TyKind, Unsafety,
|
||||||
};
|
};
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_infer::traits::{Obligation, ObligationCause};
|
use rustc_infer::traits::{Obligation, ObligationCause};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
@ -172,13 +171,8 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
|
|||||||
|
|
||||||
for arg in check_fn_args(
|
for arg in check_fn_args(
|
||||||
cx,
|
cx,
|
||||||
cx.tcx
|
cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder(),
|
||||||
.fn_sig(item.owner_id)
|
|
||||||
.instantiate_identity()
|
|
||||||
.skip_binder()
|
|
||||||
.inputs(),
|
|
||||||
sig.decl.inputs,
|
sig.decl.inputs,
|
||||||
&sig.decl.output,
|
|
||||||
&[],
|
&[],
|
||||||
)
|
)
|
||||||
.filter(|arg| arg.mutability() == Mutability::Not)
|
.filter(|arg| arg.mutability() == Mutability::Not)
|
||||||
@ -237,7 +231,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
|
|||||||
|
|
||||||
let decl = sig.decl;
|
let decl = sig.decl;
|
||||||
let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder();
|
let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder();
|
||||||
let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, &decl.output, body.params)
|
let lint_args: Vec<_> = check_fn_args(cx, sig, decl.inputs, body.params)
|
||||||
.filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
|
.filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
|
||||||
.collect();
|
.collect();
|
||||||
let results = check_ptr_arg_usage(cx, body, &lint_args);
|
let results = check_ptr_arg_usage(cx, body, &lint_args);
|
||||||
@ -443,12 +437,13 @@ impl<'tcx> DerefTy<'tcx> {
|
|||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
fn check_fn_args<'cx, 'tcx: 'cx>(
|
fn check_fn_args<'cx, 'tcx: 'cx>(
|
||||||
cx: &'cx LateContext<'tcx>,
|
cx: &'cx LateContext<'tcx>,
|
||||||
tys: &'tcx [Ty<'tcx>],
|
fn_sig: ty::FnSig<'tcx>,
|
||||||
hir_tys: &'tcx [hir::Ty<'tcx>],
|
hir_tys: &'tcx [hir::Ty<'tcx>],
|
||||||
ret_ty: &'tcx FnRetTy<'tcx>,
|
|
||||||
params: &'tcx [Param<'tcx>],
|
params: &'tcx [Param<'tcx>],
|
||||||
) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
|
) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
|
||||||
tys.iter()
|
fn_sig
|
||||||
|
.inputs()
|
||||||
|
.iter()
|
||||||
.zip(hir_tys.iter())
|
.zip(hir_tys.iter())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(move |(i, (ty, hir_ty))| {
|
.filter_map(move |(i, (ty, hir_ty))| {
|
||||||
@ -494,9 +489,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
|
|||||||
})
|
})
|
||||||
{
|
{
|
||||||
if !lifetime.is_anonymous()
|
if !lifetime.is_anonymous()
|
||||||
&& let FnRetTy::Return(ret_ty) = ret_ty
|
&& fn_sig.output()
|
||||||
&& let ret_ty = hir_ty_to_ty(cx.tcx, ret_ty)
|
|
||||||
&& ret_ty
|
|
||||||
.walk()
|
.walk()
|
||||||
.filter_map(|arg| {
|
.filter_map(|arg| {
|
||||||
arg.as_region().and_then(|lifetime| {
|
arg.as_region().and_then(|lifetime| {
|
||||||
|
@ -105,8 +105,9 @@ impl EarlyLintPass for RawStrings {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let req = {
|
let req = {
|
||||||
|
@ -3,11 +3,9 @@ use clippy_utils::is_from_proc_macro;
|
|||||||
use clippy_utils::ty::needs_ordered_drop;
|
use clippy_utils::ty::needs_ordered_drop;
|
||||||
use rustc_ast::Mutability;
|
use rustc_ast::Mutability;
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
use rustc_hir::{
|
use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath};
|
||||||
BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath,
|
|
||||||
};
|
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::{in_external_macro, is_from_async_await};
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::DesugaringKind;
|
use rustc_span::DesugaringKind;
|
||||||
@ -72,9 +70,6 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals {
|
|||||||
// the local is user-controlled
|
// the local is user-controlled
|
||||||
if !in_external_macro(cx.sess(), local.span);
|
if !in_external_macro(cx.sess(), local.span);
|
||||||
if !is_from_proc_macro(cx, expr);
|
if !is_from_proc_macro(cx, expr);
|
||||||
// Async function parameters are lowered into the closure body, so we can't lint them.
|
|
||||||
// see `lower_maybe_async_body` in `rust_ast_lowering`
|
|
||||||
if !is_from_async_await(local.span);
|
|
||||||
then {
|
then {
|
||||||
span_lint_and_help(
|
span_lint_and_help(
|
||||||
cx,
|
cx,
|
||||||
@ -111,12 +106,7 @@ fn affects_assignments(cx: &LateContext<'_>, mutability: Mutability, bind: HirId
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a rebinding of a local affects the code's drop behavior.
|
/// Check if a rebinding of a local affects the code's drop behavior.
|
||||||
fn affects_drop_behavior<'tcx>(
|
fn affects_drop_behavior<'tcx>(cx: &LateContext<'tcx>, bind: HirId, rebind: HirId, rebind_expr: &Expr<'tcx>) -> bool {
|
||||||
cx: &LateContext<'tcx>,
|
|
||||||
bind: HirId,
|
|
||||||
rebind: HirId,
|
|
||||||
rebind_expr: &Expr<'tcx>,
|
|
||||||
) -> bool {
|
|
||||||
let hir = cx.tcx.hir();
|
let hir = cx.tcx.hir();
|
||||||
|
|
||||||
// the rebinding is in a different scope than the original binding
|
// the rebinding is in a different scope than the original binding
|
||||||
|
@ -55,11 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
|
|||||||
if matches!(cx.tcx.def_kind(id.owner_id), DefKind::Impl { .. })
|
if matches!(cx.tcx.def_kind(id.owner_id), DefKind::Impl { .. })
|
||||||
&& let item = cx.tcx.hir().item(id)
|
&& let item = cx.tcx.hir().item(id)
|
||||||
&& let ItemKind::Impl(Impl {
|
&& let ItemKind::Impl(Impl {
|
||||||
items,
|
items,
|
||||||
of_trait,
|
of_trait,
|
||||||
self_ty,
|
self_ty,
|
||||||
..
|
..
|
||||||
}) = &item.kind
|
}) = &item.kind
|
||||||
&& let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
|
&& let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
|
||||||
{
|
{
|
||||||
if !map.contains_key(res) {
|
if !map.contains_key(res) {
|
||||||
|
@ -28,35 +28,43 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
match arg.kind {
|
let casts_peeled = peel_casts(arg);
|
||||||
|
match casts_peeled.kind {
|
||||||
// Catching:
|
// Catching:
|
||||||
// transmute over constants that resolve to `null`.
|
// transmute over constants that resolve to `null`.
|
||||||
ExprKind::Path(ref _qpath) if matches!(constant(cx, cx.typeck_results(), arg), Some(Constant::RawPtr(0))) => {
|
ExprKind::Path(ref _qpath)
|
||||||
|
if matches!(
|
||||||
|
constant(cx, cx.typeck_results(), casts_peeled),
|
||||||
|
Some(Constant::RawPtr(0))
|
||||||
|
) =>
|
||||||
|
{
|
||||||
lint_expr(cx, expr);
|
lint_expr(cx, expr);
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
|
|
||||||
// Catching:
|
|
||||||
// `std::mem::transmute(0 as *const i32)`
|
|
||||||
ExprKind::Cast(inner_expr, _cast_ty) if is_integer_literal(inner_expr, 0) => {
|
|
||||||
lint_expr(cx, expr);
|
|
||||||
true
|
|
||||||
},
|
|
||||||
|
|
||||||
// Catching:
|
// Catching:
|
||||||
// `std::mem::transmute(std::ptr::null::<i32>())`
|
// `std::mem::transmute(std::ptr::null::<i32>())`
|
||||||
ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => {
|
ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => {
|
||||||
lint_expr(cx, expr);
|
lint_expr(cx, expr);
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
// FIXME:
|
// FIXME:
|
||||||
// Also catch transmutations of variables which are known nulls.
|
// Also catch transmutations of variables which are known nulls.
|
||||||
// To do this, MIR const propagation seems to be the better tool.
|
// To do this, MIR const propagation seems to be the better tool.
|
||||||
// Whenever MIR const prop routines are more developed, this will
|
// Whenever MIR const prop routines are more developed, this will
|
||||||
// become available. As of this writing (25/03/19) it is not yet.
|
// become available. As of this writing (25/03/19) it is not yet.
|
||||||
|
if is_integer_literal(casts_peeled, 0) {
|
||||||
|
lint_expr(cx, expr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn peel_casts<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
|
||||||
|
match &expr.kind {
|
||||||
|
ExprKind::Cast(inner_expr, _) => peel_casts(inner_expr),
|
||||||
|
_ => expr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -315,7 +315,7 @@ impl<'tcx> LateLintPass<'tcx> for Types {
|
|||||||
fn check_fn(
|
fn check_fn(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
_: FnKind<'_>,
|
fn_kind: FnKind<'_>,
|
||||||
decl: &FnDecl<'_>,
|
decl: &FnDecl<'_>,
|
||||||
_: &Body<'_>,
|
_: &Body<'_>,
|
||||||
_: Span,
|
_: Span,
|
||||||
@ -340,6 +340,7 @@ impl<'tcx> LateLintPass<'tcx> for Types {
|
|||||||
CheckTyContext {
|
CheckTyContext {
|
||||||
is_in_trait_impl,
|
is_in_trait_impl,
|
||||||
is_exported,
|
is_exported,
|
||||||
|
in_body: matches!(fn_kind, FnKind::Closure),
|
||||||
..CheckTyContext::default()
|
..CheckTyContext::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -427,7 +428,7 @@ impl<'tcx> LateLintPass<'tcx> for Types {
|
|||||||
cx,
|
cx,
|
||||||
ty,
|
ty,
|
||||||
CheckTyContext {
|
CheckTyContext {
|
||||||
is_local: true,
|
in_body: true,
|
||||||
..CheckTyContext::default()
|
..CheckTyContext::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -481,7 +482,7 @@ impl Types {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match hir_ty.kind {
|
match hir_ty.kind {
|
||||||
TyKind::Path(ref qpath) if !context.is_local => {
|
TyKind::Path(ref qpath) if !context.in_body => {
|
||||||
let hir_id = hir_ty.hir_id;
|
let hir_id = hir_ty.hir_id;
|
||||||
let res = cx.qpath_res(qpath, hir_id);
|
let res = cx.qpath_res(qpath, hir_id);
|
||||||
if let Some(def_id) = res.opt_def_id() {
|
if let Some(def_id) = res.opt_def_id() {
|
||||||
@ -581,8 +582,8 @@ impl Types {
|
|||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
struct CheckTyContext {
|
struct CheckTyContext {
|
||||||
is_in_trait_impl: bool,
|
is_in_trait_impl: bool,
|
||||||
/// `true` for types on local variables.
|
/// `true` for types on local variables and in closure signatures.
|
||||||
is_local: bool,
|
in_body: bool,
|
||||||
/// `true` for types that are part of the public API.
|
/// `true` for types that are part of the public API.
|
||||||
is_exported: bool,
|
is_exported: bool,
|
||||||
is_nested_call: bool,
|
is_nested_call: bool,
|
||||||
|
@ -7,7 +7,7 @@ use rustc_errors::Applicability;
|
|||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind};
|
use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind};
|
||||||
use rustc_lint::{LateContext, LintContext};
|
use rustc_lint::{LateContext, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::{in_external_macro, is_from_async_await};
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
|
|
||||||
use super::LET_UNIT_VALUE;
|
use super::LET_UNIT_VALUE;
|
||||||
@ -16,6 +16,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
|
|||||||
if let Some(init) = local.init
|
if let Some(init) = local.init
|
||||||
&& !local.pat.span.from_expansion()
|
&& !local.pat.span.from_expansion()
|
||||||
&& !in_external_macro(cx.sess(), local.span)
|
&& !in_external_macro(cx.sess(), local.span)
|
||||||
|
&& !is_from_async_await(local.span)
|
||||||
&& cx.typeck_results().pat_ty(local.pat).is_unit()
|
&& cx.typeck_results().pat_ty(local.pat).is_unit()
|
||||||
{
|
{
|
||||||
if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer))
|
if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer))
|
||||||
|
93
clippy_lints/src/unnecessary_map_on_constructor.rs
Normal file
93
clippy_lints/src/unnecessary_map_on_constructor.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
|
use clippy_utils::ty::get_type_diagnostic_name;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
|
use rustc_span::sym;
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Suggest removing the use of a may (or map_err) method when an Option or Result is being construted.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// It introduces unnecessary complexity. In this case the function can be used directly and
|
||||||
|
/// construct the Option or Result from the output.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// Some(4).map(i32::swap_bytes);
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// Some(i32::swap_bytes(4));
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.73.0"]
|
||||||
|
pub UNNECESSARY_MAP_ON_CONSTRUCTOR,
|
||||||
|
complexity,
|
||||||
|
"using `map`/`map_err` on `Option` or `Result` constructors"
|
||||||
|
}
|
||||||
|
declare_lint_pass!(UnnecessaryMapOnConstructor => [UNNECESSARY_MAP_ON_CONSTRUCTOR]);
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
|
||||||
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
|
||||||
|
if expr.span.from_expansion() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let hir::ExprKind::MethodCall(path, recv, args, ..) = expr.kind
|
||||||
|
&& let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)){
|
||||||
|
let (constructor_path, constructor_item) =
|
||||||
|
if let hir::ExprKind::Call(constructor, constructor_args) = recv.kind
|
||||||
|
&& let hir::ExprKind::Path(constructor_path) = constructor.kind
|
||||||
|
&& let Some(arg) = constructor_args.get(0)
|
||||||
|
{
|
||||||
|
if constructor.span.from_expansion() || arg.span.from_expansion() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(constructor_path, arg)
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let constructor_symbol = match constructor_path {
|
||||||
|
hir::QPath::Resolved(_, path) => {
|
||||||
|
if let Some(path_segment) = path.segments.last() {
|
||||||
|
path_segment.ident.name
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hir::QPath::TypeRelative(_, path) => path.ident.name,
|
||||||
|
hir::QPath::LangItem(_, _, _) => return,
|
||||||
|
};
|
||||||
|
match constructor_symbol {
|
||||||
|
sym::Some | sym::Ok if path.ident.name == rustc_span::sym::map => (),
|
||||||
|
sym::Err if path.ident.name == sym!(map_err) => (),
|
||||||
|
_ => return,
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(map_arg) = args.get(0)
|
||||||
|
&& let hir::ExprKind::Path(fun) = map_arg.kind
|
||||||
|
{
|
||||||
|
if map_arg.span.from_expansion() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
let fun_snippet = snippet_with_applicability(cx, fun.span(), "_", &mut applicability);
|
||||||
|
let constructor_snippet =
|
||||||
|
snippet_with_applicability(cx, constructor_path.span(), "_", &mut applicability);
|
||||||
|
let constructor_arg_snippet =
|
||||||
|
snippet_with_applicability(cx, constructor_item.span, "_", &mut applicability);
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
UNNECESSARY_MAP_ON_CONSTRUCTOR,
|
||||||
|
expr.span,
|
||||||
|
&format!("unnecessary {} on constructor {constructor_snippet}(_)", path.ident.name),
|
||||||
|
"try",
|
||||||
|
format!("{constructor_snippet}({fun_snippet}({constructor_arg_snippet}))"),
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,10 +8,14 @@ use rustc_errors::Applicability;
|
|||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
|
use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
|
||||||
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
|
use rustc_infer::traits::Obligation;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty;
|
use rustc_middle::traits::ObligationCause;
|
||||||
|
use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt};
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
@ -61,22 +65,69 @@ impl MethodOrFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the span of the `IntoIterator` trait bound in the function pointed to by `fn_did`
|
/// Returns the span of the `IntoIterator` trait bound in the function pointed to by `fn_did`,
|
||||||
fn into_iter_bound(cx: &LateContext<'_>, fn_did: DefId, into_iter_did: DefId, param_index: u32) -> Option<Span> {
|
/// iff all of the bounds also hold for the type of the `.into_iter()` receiver.
|
||||||
cx.tcx
|
/// ```ignore
|
||||||
.predicates_of(fn_did)
|
/// pub fn foo<I>(i: I)
|
||||||
.predicates
|
/// where I: IntoIterator<Item=i32> + ExactSizeIterator
|
||||||
.iter()
|
/// ^^^^^^^^^^^^^^^^^ this extra bound stops us from suggesting to remove `.into_iter()` ...
|
||||||
.find_map(|&(ref pred, span)| {
|
/// {
|
||||||
if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder()
|
/// assert_eq!(i.len(), 3);
|
||||||
&& tr.def_id() == into_iter_did
|
/// }
|
||||||
&& tr.self_ty().is_param(param_index)
|
///
|
||||||
{
|
/// pub fn bar() {
|
||||||
Some(span)
|
/// foo([1, 2, 3].into_iter());
|
||||||
} else {
|
/// ^^^^^^^^^^^^ ... here, because `[i32; 3]` is not `ExactSizeIterator`
|
||||||
None
|
/// }
|
||||||
|
/// ```
|
||||||
|
fn into_iter_bound<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
fn_did: DefId,
|
||||||
|
into_iter_did: DefId,
|
||||||
|
into_iter_receiver: Ty<'tcx>,
|
||||||
|
param_index: u32,
|
||||||
|
node_args: GenericArgsRef<'tcx>,
|
||||||
|
) -> Option<Span> {
|
||||||
|
let param_env = cx.tcx.param_env(fn_did);
|
||||||
|
let mut into_iter_span = None;
|
||||||
|
|
||||||
|
for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates {
|
||||||
|
if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() {
|
||||||
|
if tr.self_ty().is_param(param_index) {
|
||||||
|
if tr.def_id() == into_iter_did {
|
||||||
|
into_iter_span = Some(*span);
|
||||||
|
} else {
|
||||||
|
let tr = cx.tcx.erase_regions(tr);
|
||||||
|
if tr.has_escaping_bound_vars() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Substitute generics in the predicate and replace the IntoIterator type parameter with the
|
||||||
|
// `.into_iter()` receiver to see if the bound also holds for that type.
|
||||||
|
let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| {
|
||||||
|
if i == param_index as usize {
|
||||||
|
GenericArg::from(into_iter_receiver)
|
||||||
|
} else {
|
||||||
|
arg
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args);
|
||||||
|
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate);
|
||||||
|
if !cx
|
||||||
|
.tcx
|
||||||
|
.infer_ctxt()
|
||||||
|
.build()
|
||||||
|
.predicate_must_hold_modulo_regions(&obligation)
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
into_iter_span
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts the receiver of a `.into_iter()` method call.
|
/// Extracts the receiver of a `.into_iter()` method call.
|
||||||
@ -160,22 +211,41 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||||||
// `fn_sig` does not ICE. (see #11065)
|
// `fn_sig` does not ICE. (see #11065)
|
||||||
&& cx.tcx.opt_def_kind(did).is_some_and(DefKind::is_fn_like) =>
|
&& cx.tcx.opt_def_kind(did).is_some_and(DefKind::is_fn_like) =>
|
||||||
{
|
{
|
||||||
Some((did, args, MethodOrFunction::Function))
|
Some((
|
||||||
|
did,
|
||||||
|
args,
|
||||||
|
cx.typeck_results().node_args(recv.hir_id),
|
||||||
|
MethodOrFunction::Function
|
||||||
|
))
|
||||||
}
|
}
|
||||||
ExprKind::MethodCall(.., args, _) => {
|
ExprKind::MethodCall(.., args, _) => {
|
||||||
cx.typeck_results().type_dependent_def_id(parent.hir_id)
|
cx.typeck_results().type_dependent_def_id(parent.hir_id)
|
||||||
.map(|did| (did, args, MethodOrFunction::Method))
|
.map(|did| {
|
||||||
|
return (
|
||||||
|
did,
|
||||||
|
args,
|
||||||
|
cx.typeck_results().node_args(parent.hir_id),
|
||||||
|
MethodOrFunction::Method
|
||||||
|
);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((parent_fn_did, args, kind)) = parent_fn
|
if let Some((parent_fn_did, args, node_args, kind)) = parent_fn
|
||||||
&& let Some(into_iter_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
|
&& let Some(into_iter_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
|
||||||
&& let sig = cx.tcx.fn_sig(parent_fn_did).skip_binder().skip_binder()
|
&& let sig = cx.tcx.fn_sig(parent_fn_did).skip_binder().skip_binder()
|
||||||
&& let Some(arg_pos) = args.iter().position(|x| x.hir_id == e.hir_id)
|
&& let Some(arg_pos) = args.iter().position(|x| x.hir_id == e.hir_id)
|
||||||
&& let Some(&into_iter_param) = sig.inputs().get(kind.param_pos(arg_pos))
|
&& let Some(&into_iter_param) = sig.inputs().get(kind.param_pos(arg_pos))
|
||||||
&& let ty::Param(param) = into_iter_param.kind()
|
&& let ty::Param(param) = into_iter_param.kind()
|
||||||
&& let Some(span) = into_iter_bound(cx, parent_fn_did, into_iter_did, param.index)
|
&& let Some(span) = into_iter_bound(
|
||||||
|
cx,
|
||||||
|
parent_fn_did,
|
||||||
|
into_iter_did,
|
||||||
|
cx.typeck_results().expr_ty(into_iter_recv),
|
||||||
|
param.index,
|
||||||
|
node_args
|
||||||
|
)
|
||||||
&& self.expn_depth == 0
|
&& self.expn_depth == 0
|
||||||
{
|
{
|
||||||
// Get the "innermost" `.into_iter()` call, e.g. given this expression:
|
// Get the "innermost" `.into_iter()` call, e.g. given this expression:
|
||||||
|
@ -542,11 +542,11 @@ define_Conf! {
|
|||||||
/// Lint: UNDOCUMENTED_UNSAFE_BLOCKS.
|
/// Lint: UNDOCUMENTED_UNSAFE_BLOCKS.
|
||||||
///
|
///
|
||||||
/// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block
|
/// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block
|
||||||
(accept_comment_above_statement: bool = false),
|
(accept_comment_above_statement: bool = true),
|
||||||
/// Lint: UNDOCUMENTED_UNSAFE_BLOCKS.
|
/// Lint: UNDOCUMENTED_UNSAFE_BLOCKS.
|
||||||
///
|
///
|
||||||
/// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block
|
/// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block
|
||||||
(accept_comment_above_attributes: bool = false),
|
(accept_comment_above_attributes: bool = true),
|
||||||
/// Lint: UNNECESSARY_RAW_STRING_HASHES.
|
/// Lint: UNNECESSARY_RAW_STRING_HASHES.
|
||||||
///
|
///
|
||||||
/// Whether to allow `r#""#` when `r""` can be used
|
/// Whether to allow `r#""#` when `r""` can be used
|
||||||
@ -561,6 +561,11 @@ define_Conf! {
|
|||||||
/// Which crates to allow absolute paths from
|
/// Which crates to allow absolute paths from
|
||||||
(absolute_paths_allowed_crates: rustc_data_structures::fx::FxHashSet<String> =
|
(absolute_paths_allowed_crates: rustc_data_structures::fx::FxHashSet<String> =
|
||||||
rustc_data_structures::fx::FxHashSet::default()),
|
rustc_data_structures::fx::FxHashSet::default()),
|
||||||
|
/// Lint: PATH_ENDS_WITH_EXT.
|
||||||
|
///
|
||||||
|
/// Additional dotfiles (files or directories starting with a dot) to allow
|
||||||
|
(allowed_dotfiles: rustc_data_structures::fx::FxHashSet<String> =
|
||||||
|
rustc_data_structures::fx::FxHashSet::default()),
|
||||||
/// Lint: EXPLICIT_ITER_LOOP
|
/// Lint: EXPLICIT_ITER_LOOP
|
||||||
///
|
///
|
||||||
/// Whether to recommend using implicit into iter for reborrowed values.
|
/// Whether to recommend using implicit into iter for reborrowed values.
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
use clippy_utils::macros::collect_ast_format_args;
|
use clippy_utils::macros::AST_FORMAT_ARGS;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_ast::{Expr, ExprKind, FormatArgs};
|
use rustc_ast::{Crate, Expr, ExprKind, FormatArgs};
|
||||||
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_lexer::{tokenize, TokenKind};
|
use rustc_lexer::{tokenize, TokenKind};
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::hygiene;
|
use rustc_span::{hygiene, Span};
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
|
use std::mem;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
@ -17,7 +20,12 @@ declare_clippy_lint! {
|
|||||||
"collects `format_args` AST nodes for use in later lints"
|
"collects `format_args` AST nodes for use in later lints"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
|
#[derive(Default)]
|
||||||
|
pub struct FormatArgsCollector {
|
||||||
|
format_args: FxHashMap<Span, Rc<FormatArgs>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
|
||||||
|
|
||||||
impl EarlyLintPass for FormatArgsCollector {
|
impl EarlyLintPass for FormatArgsCollector {
|
||||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||||
@ -26,9 +34,17 @@ impl EarlyLintPass for FormatArgsCollector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
collect_ast_format_args(expr.span, args);
|
self.format_args
|
||||||
|
.insert(expr.span.with_parent(None), Rc::new((**args).clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) {
|
||||||
|
AST_FORMAT_ARGS.with(|ast_format_args| {
|
||||||
|
let result = ast_format_args.set(mem::take(&mut self.format_args));
|
||||||
|
debug_assert!(result.is_ok());
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Detects if the format string or an argument has its span set by a proc macro to something inside
|
/// Detects if the format string or an argument has its span set by a proc macro to something inside
|
||||||
|
@ -10,7 +10,7 @@ use rustc_hir::def::{DefKind, Res};
|
|||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::mir::interpret::ConstValue;
|
use rustc_middle::mir::ConstValue;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
|
@ -5,10 +5,9 @@ use if_chain::if_chain;
|
|||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::Item;
|
use rustc_hir::Item;
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::fast_reject::SimplifiedType;
|
use rustc_middle::ty::fast_reject::SimplifiedType;
|
||||||
use rustc_middle::ty::{self, FloatTy};
|
use rustc_middle::ty::FloatTy;
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
|
|
||||||
@ -34,25 +33,20 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
|
|||||||
let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
|
let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if mod_name.as_str() == "paths";
|
if mod_name.as_str() == "paths";
|
||||||
if let hir::ItemKind::Const(ty, _, body_id) = item.kind;
|
if let hir::ItemKind::Const(.., body_id) = item.kind;
|
||||||
let ty = hir_ty_to_ty(cx.tcx, ty);
|
|
||||||
if let ty::Array(el_ty, _) = &ty.kind();
|
|
||||||
if let ty::Ref(_, el_ty, _) = &el_ty.kind();
|
|
||||||
if el_ty.is_str();
|
|
||||||
let body = cx.tcx.hir().body(body_id);
|
let body = cx.tcx.hir().body(body_id);
|
||||||
let typeck_results = cx.tcx.typeck_body(body_id);
|
let typeck_results = cx.tcx.typeck_body(body_id);
|
||||||
if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value);
|
if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value);
|
||||||
let path: Vec<&str> = path
|
if let Some(path) = path
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
if let Constant::Str(s) = x {
|
if let Constant::Str(s) = x {
|
||||||
s.as_str()
|
Some(s.as_str())
|
||||||
} else {
|
} else {
|
||||||
// We checked the type of the constant above
|
None
|
||||||
unreachable!()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect::<Option<Vec<&str>>>();
|
||||||
if !check_path(cx, &path[..]);
|
if !check_path(cx, &path[..]);
|
||||||
then {
|
then {
|
||||||
span_lint(cx, INVALID_PATHS, item.span, "invalid path");
|
span_lint(cx, INVALID_PATHS, item.span, "invalid path");
|
||||||
|
@ -31,7 +31,7 @@ use serde::{Serialize, Serializer};
|
|||||||
use std::collections::{BTreeSet, BinaryHeap};
|
use std::collections::{BTreeSet, BinaryHeap};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
use std::fs::{self, OpenOptions};
|
use std::fs::{self, File};
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@ -229,25 +229,10 @@ impl Drop for MetadataCollector {
|
|||||||
collect_renames(&mut lints);
|
collect_renames(&mut lints);
|
||||||
|
|
||||||
// Outputting json
|
// Outputting json
|
||||||
if Path::new(JSON_OUTPUT_FILE).exists() {
|
fs::write(JSON_OUTPUT_FILE, serde_json::to_string_pretty(&lints).unwrap()).unwrap();
|
||||||
fs::remove_file(JSON_OUTPUT_FILE).unwrap();
|
|
||||||
}
|
|
||||||
let mut file = OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.open(JSON_OUTPUT_FILE)
|
|
||||||
.unwrap();
|
|
||||||
writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap();
|
|
||||||
|
|
||||||
// Outputting markdown
|
// Outputting markdown
|
||||||
if Path::new(MARKDOWN_OUTPUT_FILE).exists() {
|
let mut file = File::create(MARKDOWN_OUTPUT_FILE).unwrap();
|
||||||
fs::remove_file(MARKDOWN_OUTPUT_FILE).unwrap();
|
|
||||||
}
|
|
||||||
let mut file = OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.open(MARKDOWN_OUTPUT_FILE)
|
|
||||||
.unwrap();
|
|
||||||
writeln!(
|
writeln!(
|
||||||
file,
|
file,
|
||||||
"<!--
|
"<!--
|
||||||
@ -261,17 +246,15 @@ Please use that command to update the file and do not edit it by hand.
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Write configuration links to CHANGELOG.md
|
// Write configuration links to CHANGELOG.md
|
||||||
let mut changelog = std::fs::read_to_string(CHANGELOG_PATH).unwrap();
|
let changelog = std::fs::read_to_string(CHANGELOG_PATH).unwrap();
|
||||||
let mut changelog_file = OpenOptions::new().read(true).write(true).open(CHANGELOG_PATH).unwrap();
|
let mut changelog_file = File::create(CHANGELOG_PATH).unwrap();
|
||||||
|
let position = changelog
|
||||||
if let Some(position) = changelog.find("<!-- begin autogenerated links to configuration documentation -->") {
|
.find("<!-- begin autogenerated links to configuration documentation -->")
|
||||||
// I know this is kinda wasteful, we just don't have regex on `clippy_lints` so... this is the best
|
.unwrap();
|
||||||
// we can do AFAIK.
|
|
||||||
changelog = changelog[..position].to_string();
|
|
||||||
}
|
|
||||||
writeln!(
|
writeln!(
|
||||||
changelog_file,
|
changelog_file,
|
||||||
"{changelog}<!-- begin autogenerated links to configuration documentation -->\n{}\n<!-- end autogenerated links to configuration documentation -->",
|
"{}<!-- begin autogenerated links to configuration documentation -->\n{}\n<!-- end autogenerated links to configuration documentation -->",
|
||||||
|
&changelog[..position],
|
||||||
self.configs_to_markdown(ClippyConfiguration::to_markdown_link)
|
self.configs_to_markdown(ClippyConfiguration::to_markdown_link)
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -5,9 +5,8 @@ use clippy_utils::{match_def_path, paths};
|
|||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir_analysis::hir_ty_to_ty;
|
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::ty::{self, GenericArgKind};
|
use rustc_middle::ty::{self, EarlyBinder, GenericArgKind};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
@ -25,16 +24,14 @@ impl LateLintPass<'_> for MsrvAttrImpl {
|
|||||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let hir::ItemKind::Impl(hir::Impl {
|
if let hir::ItemKind::Impl(hir::Impl {
|
||||||
of_trait: Some(lint_pass_trait_ref),
|
of_trait: Some(_),
|
||||||
self_ty,
|
|
||||||
items,
|
items,
|
||||||
..
|
..
|
||||||
}) = &item.kind;
|
}) = &item.kind;
|
||||||
if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id();
|
if let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::instantiate_identity);
|
||||||
let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS);
|
let is_late_pass = match_def_path(cx, trait_ref.def_id, &paths::LATE_LINT_PASS);
|
||||||
if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS);
|
if is_late_pass || match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS);
|
||||||
let self_ty = hir_ty_to_ty(cx.tcx, self_ty);
|
if let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind();
|
||||||
if let ty::Adt(self_ty_def, _) = self_ty.kind();
|
|
||||||
if self_ty_def.is_struct();
|
if self_ty_def.is_struct();
|
||||||
if self_ty_def.all_fields().any(|f| {
|
if self_ty_def.all_fields().any(|f| {
|
||||||
cx.tcx
|
cx.tcx
|
||||||
|
@ -10,7 +10,8 @@ use rustc_hir::def::{DefKind, Res};
|
|||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::{Expr, ExprKind, Local, Mutability, Node};
|
use rustc_hir::{Expr, ExprKind, Local, Mutability, Node};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc};
|
use rustc_middle::mir::interpret::{Allocation, GlobalAlloc};
|
||||||
|
use rustc_middle::mir::ConstValue;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
@ -232,7 +233,8 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
|
|||||||
cx.tcx.type_of(def_id).instantiate_identity(),
|
cx.tcx.type_of(def_id).instantiate_identity(),
|
||||||
),
|
),
|
||||||
Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? {
|
Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? {
|
||||||
ConstValue::Indirect { alloc, offset } if offset.bytes() == 0 => {
|
ConstValue::Indirect { alloc_id, offset } if offset.bytes() == 0 => {
|
||||||
|
let alloc = cx.tcx.global_alloc(alloc_id).unwrap_memory();
|
||||||
read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity())
|
read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity())
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -304,7 +304,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
|
|||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
|
|
||||||
find_format_args(cx, expr, macro_call.expn, |format_args| {
|
if let Some(format_args) = find_format_args(cx, expr, macro_call.expn) {
|
||||||
// ignore `writeln!(w)` and `write!(v, some_macro!())`
|
// ignore `writeln!(w)` and `write!(v, some_macro!())`
|
||||||
if format_args.span.from_expansion() {
|
if format_args.span.from_expansion() {
|
||||||
return;
|
return;
|
||||||
@ -312,15 +312,15 @@ impl<'tcx> LateLintPass<'tcx> for Write {
|
|||||||
|
|
||||||
match diag_name {
|
match diag_name {
|
||||||
sym::print_macro | sym::eprint_macro | sym::write_macro => {
|
sym::print_macro | sym::eprint_macro | sym::write_macro => {
|
||||||
check_newline(cx, format_args, ¯o_call, name);
|
check_newline(cx, &format_args, ¯o_call, name);
|
||||||
},
|
},
|
||||||
sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
|
sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
|
||||||
check_empty_string(cx, format_args, ¯o_call, name);
|
check_empty_string(cx, &format_args, ¯o_call, name);
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
check_literal(cx, format_args, name);
|
check_literal(cx, &format_args, name);
|
||||||
|
|
||||||
if !self.in_debug_impl {
|
if !self.in_debug_impl {
|
||||||
for piece in &format_args.template {
|
for piece in &format_args.template {
|
||||||
@ -334,7 +334,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,10 +671,11 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
|
|||||||
ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
|
ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
mir::Const::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => {
|
mir::Const::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) =>
|
||||||
|
{
|
||||||
let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?;
|
let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?;
|
||||||
String::from_utf8(data.to_owned()).ok().map(Constant::Str)
|
String::from_utf8(data.to_owned()).ok().map(Constant::Str)
|
||||||
}
|
},
|
||||||
mir::Const::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => {
|
mir::Const::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => {
|
||||||
let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory();
|
let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory();
|
||||||
match result.ty().kind() {
|
match result.ty().kind() {
|
||||||
|
@ -83,9 +83,9 @@ pub fn span_lint_and_help<T: LintContext>(
|
|||||||
cx.struct_span_lint(lint, span, msg.to_string(), |diag| {
|
cx.struct_span_lint(lint, span, msg.to_string(), |diag| {
|
||||||
let help = help.to_string();
|
let help = help.to_string();
|
||||||
if let Some(help_span) = help_span {
|
if let Some(help_span) = help_span {
|
||||||
diag.span_help(help_span, help.to_string());
|
diag.span_help(help_span, help);
|
||||||
} else {
|
} else {
|
||||||
diag.help(help.to_string());
|
diag.help(help);
|
||||||
}
|
}
|
||||||
docs_link(diag, lint);
|
docs_link(diag, lint);
|
||||||
diag
|
diag
|
||||||
|
@ -1785,6 +1785,33 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the lint is `#[allow]`ed or `#[expect]`ed at any of the `ids`, fulfilling all
|
||||||
|
/// of the expectations in `ids`
|
||||||
|
///
|
||||||
|
/// This should only be used when the lint would otherwise be emitted, for a way to check if a lint
|
||||||
|
/// is allowed early to skip work see [`is_lint_allowed`]
|
||||||
|
///
|
||||||
|
/// To emit at a lint at a different context than the one current see
|
||||||
|
/// [`span_lint_hir`](diagnostics::span_lint_hir) or
|
||||||
|
/// [`span_lint_hir_and_then`](diagnostics::span_lint_hir_and_then)
|
||||||
|
pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
|
||||||
|
let mut suppress_lint = false;
|
||||||
|
|
||||||
|
for id in ids {
|
||||||
|
let (level, _) = cx.tcx.lint_level_at_node(lint, id);
|
||||||
|
if let Some(expectation) = level.get_expectation_id() {
|
||||||
|
cx.fulfill_expectation(expectation);
|
||||||
|
}
|
||||||
|
|
||||||
|
match level {
|
||||||
|
Level::Allow | Level::Expect(_) => suppress_lint = true,
|
||||||
|
Level::Warn | Level::ForceWarn(_) | Level::Deny | Level::Forbid => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suppress_lint
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if the lint is allowed in the current context. This is useful for
|
/// Returns `true` if the lint is allowed in the current context. This is useful for
|
||||||
/// skipping long running code when it's unnecessary
|
/// skipping long running code when it's unnecessary
|
||||||
///
|
///
|
||||||
@ -1958,7 +1985,7 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
|
|||||||
/// Checks if the given function kind is an async function.
|
/// Checks if the given function kind is an async function.
|
||||||
pub fn is_async_fn(kind: FnKind<'_>) -> bool {
|
pub fn is_async_fn(kind: FnKind<'_>) -> bool {
|
||||||
match kind {
|
match kind {
|
||||||
FnKind::ItemFn(_, _, header) => header.asyncness .is_async(),
|
FnKind::ItemFn(_, _, header) => header.asyncness.is_async(),
|
||||||
FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
|
FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
|
||||||
FnKind::Closure => false,
|
FnKind::Closure => false,
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,9 @@ use rustc_lint::LateContext;
|
|||||||
use rustc_span::def_id::DefId;
|
use rustc_span::def_id::DefId;
|
||||||
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
|
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
|
||||||
use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol};
|
use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol};
|
||||||
use std::cell::RefCell;
|
use std::cell::OnceCell;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
|
const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
|
||||||
@ -374,28 +375,21 @@ thread_local! {
|
|||||||
/// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
|
/// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
|
||||||
/// assumption that the early pass that populates the map and the later late passes will all be
|
/// assumption that the early pass that populates the map and the later late passes will all be
|
||||||
/// running on the same thread.
|
/// running on the same thread.
|
||||||
static AST_FORMAT_ARGS: RefCell<FxHashMap<Span, FormatArgs>> = {
|
#[doc(hidden)]
|
||||||
|
pub static AST_FORMAT_ARGS: OnceCell<FxHashMap<Span, Rc<FormatArgs>>> = {
|
||||||
static CALLED: AtomicBool = AtomicBool::new(false);
|
static CALLED: AtomicBool = AtomicBool::new(false);
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
!CALLED.swap(true, Ordering::SeqCst),
|
!CALLED.swap(true, Ordering::SeqCst),
|
||||||
"incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
|
"incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
|
||||||
);
|
);
|
||||||
|
|
||||||
RefCell::default()
|
OnceCell::new()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by
|
/// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of
|
||||||
/// `FormatArgsCollector`
|
/// `expn_id`
|
||||||
pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) {
|
pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<Rc<FormatArgs>> {
|
||||||
AST_FORMAT_ARGS.with(|ast_format_args| {
|
|
||||||
ast_format_args.borrow_mut().insert(span, format_args.clone());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calls `callback` with an AST [`FormatArgs`] node if a `format_args` expansion is found as a
|
|
||||||
/// descendant of `expn_id`
|
|
||||||
pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) {
|
|
||||||
let format_args_expr = for_each_expr(start, |expr| {
|
let format_args_expr = for_each_expr(start, |expr| {
|
||||||
let ctxt = expr.span.ctxt();
|
let ctxt = expr.span.ctxt();
|
||||||
if ctxt.outer_expn().is_descendant_of(expn_id) {
|
if ctxt.outer_expn().is_descendant_of(expn_id) {
|
||||||
@ -410,13 +404,14 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId,
|
|||||||
} else {
|
} else {
|
||||||
ControlFlow::Continue(Descend::No)
|
ControlFlow::Continue(Descend::No)
|
||||||
}
|
}
|
||||||
});
|
})?;
|
||||||
|
|
||||||
if let Some(expr) = format_args_expr {
|
AST_FORMAT_ARGS.with(|ast_format_args| {
|
||||||
AST_FORMAT_ARGS.with(|ast_format_args| {
|
ast_format_args
|
||||||
ast_format_args.borrow().get(&expr.span).map(callback);
|
.get()?
|
||||||
});
|
.get(&format_args_expr.span.with_parent(None))
|
||||||
}
|
.map(Rc::clone)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
|
/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use rustc_hir::{Expr, HirId};
|
use rustc_hir::{Expr, HirId};
|
||||||
|
use rustc_index::bit_set::BitSet;
|
||||||
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
||||||
use rustc_middle::mir::{
|
use rustc_middle::mir::{
|
||||||
traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK,
|
traversal, BasicBlock, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
|
||||||
@ -79,8 +80,32 @@ impl<'a, 'tcx> Visitor<'tcx> for V<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the block is part of a cycle
|
||||||
|
pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool {
|
||||||
|
let mut seen = BitSet::new_empty(body.basic_blocks.len());
|
||||||
|
let mut to_visit = Vec::with_capacity(body.basic_blocks.len() / 2);
|
||||||
|
|
||||||
|
seen.insert(block);
|
||||||
|
let mut next = block;
|
||||||
|
loop {
|
||||||
|
for succ in body.basic_blocks[next].terminator().successors() {
|
||||||
|
if seen.insert(succ) {
|
||||||
|
to_visit.push(succ);
|
||||||
|
} else if succ == block {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(x) = to_visit.pop() {
|
||||||
|
next = x;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Convenience wrapper around `visit_local_usage`.
|
/// Convenience wrapper around `visit_local_usage`.
|
||||||
pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option<bool> {
|
pub fn used_exactly_once(mir: &Body<'_>, local: rustc_middle::mir::Local) -> Option<bool> {
|
||||||
visit_local_usage(
|
visit_local_usage(
|
||||||
&[local],
|
&[local],
|
||||||
mir,
|
mir,
|
||||||
@ -91,11 +116,14 @@ pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle:
|
|||||||
)
|
)
|
||||||
.map(|mut vec| {
|
.map(|mut vec| {
|
||||||
let LocalUsage { local_use_locs, .. } = vec.remove(0);
|
let LocalUsage { local_use_locs, .. } = vec.remove(0);
|
||||||
local_use_locs
|
let mut locations = local_use_locs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|location| !is_local_assignment(mir, local, *location))
|
.filter(|&location| !is_local_assignment(mir, local, location));
|
||||||
.count()
|
if let Some(location) = locations.next() {
|
||||||
== 1
|
locations.next().is_none() && !block_in_cycle(mir, location.block)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
|
|||||||
use rustc_lint::{LateContext, LintContext};
|
use rustc_lint::{LateContext, LintContext};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::source_map::{original_sp, SourceMap};
|
use rustc_span::source_map::{original_sp, SourceMap};
|
||||||
use rustc_span::{hygiene, BytePos, SourceFileAndLine, Pos, SourceFile, Span, SpanData, SyntaxContext, DUMMY_SP};
|
use rustc_span::{hygiene, BytePos, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, DUMMY_SP};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
|
@ -13,7 +13,8 @@ use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
|
|||||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::mir::{ConstValue, interpret::Scalar};
|
use rustc_middle::mir::interpret::Scalar;
|
||||||
|
use rustc_middle::mir::ConstValue;
|
||||||
use rustc_middle::traits::EvaluationResult;
|
use rustc_middle::traits::EvaluationResult;
|
||||||
use rustc_middle::ty::layout::ValidityRequirement;
|
use rustc_middle::ty::layout::ValidityRequirement;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
|
@ -208,8 +208,7 @@ fn path_segment_certainty(
|
|||||||
if cx.tcx.res_generics_def_id(path_segment.res).is_some() {
|
if cx.tcx.res_generics_def_id(path_segment.res).is_some() {
|
||||||
let generics = cx.tcx.generics_of(def_id);
|
let generics = cx.tcx.generics_of(def_id);
|
||||||
let count = generics.params.len() - generics.host_effect_index.is_some() as usize;
|
let count = generics.params.len() - generics.host_effect_index.is_some() as usize;
|
||||||
let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && count == 0
|
let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && count == 0 {
|
||||||
{
|
|
||||||
Certainty::Certain(None)
|
Certainty::Certain(None)
|
||||||
} else {
|
} else {
|
||||||
Certainty::Uncertain
|
Certainty::Uncertain
|
||||||
@ -300,10 +299,11 @@ fn type_is_inferrable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> b
|
|||||||
|
|
||||||
// Check that all type parameters appear in the functions input types.
|
// Check that all type parameters appear in the functions input types.
|
||||||
(0..(generics.parent_count + generics.params.len()) as u32).all(|index| {
|
(0..(generics.parent_count + generics.params.len()) as u32).all(|index| {
|
||||||
Some(index as usize) == generics.host_effect_index || fn_sig
|
Some(index as usize) == generics.host_effect_index
|
||||||
.inputs()
|
|| fn_sig
|
||||||
.iter()
|
.inputs()
|
||||||
.any(|input_ty| contains_param(*input_ty.skip_binder(), index))
|
.iter()
|
||||||
|
.any(|input_ty| contains_param(*input_ty.skip_binder(), index))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ use test_utils::IS_RUSTC_TEST_SUITE;
|
|||||||
// in the depinfo file (otherwise cargo thinks they are unused)
|
// in the depinfo file (otherwise cargo thinks they are unused)
|
||||||
extern crate clippy_lints;
|
extern crate clippy_lints;
|
||||||
extern crate clippy_utils;
|
extern crate clippy_utils;
|
||||||
extern crate derive_new;
|
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
extern crate if_chain;
|
extern crate if_chain;
|
||||||
extern crate itertools;
|
extern crate itertools;
|
||||||
@ -33,7 +32,6 @@ mod test_utils;
|
|||||||
static TEST_DEPENDENCIES: &[&str] = &[
|
static TEST_DEPENDENCIES: &[&str] = &[
|
||||||
"clippy_lints",
|
"clippy_lints",
|
||||||
"clippy_utils",
|
"clippy_utils",
|
||||||
"derive_new",
|
|
||||||
"futures",
|
"futures",
|
||||||
"if_chain",
|
"if_chain",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
1
tests/ui-toml/decimal_literal_representation/clippy.toml
Normal file
1
tests/ui-toml/decimal_literal_representation/clippy.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
literal-representation-threshold = 0xFFFFFF
|
@ -0,0 +1,6 @@
|
|||||||
|
#![warn(clippy::decimal_literal_representation)]
|
||||||
|
fn main() {
|
||||||
|
let _ = 8388608;
|
||||||
|
let _ = 0x00FF_FFFF;
|
||||||
|
//~^ ERROR: integer literal has a better hexadecimal representation
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
#![warn(clippy::decimal_literal_representation)]
|
||||||
|
fn main() {
|
||||||
|
let _ = 8388608;
|
||||||
|
let _ = 16777215;
|
||||||
|
//~^ ERROR: integer literal has a better hexadecimal representation
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
error: integer literal has a better hexadecimal representation
|
||||||
|
--> $DIR/decimal_literal_representation.rs:4:13
|
||||||
|
|
|
||||||
|
LL | let _ = 16777215;
|
||||||
|
| ^^^^^^^^ help: consider: `0x00FF_FFFF`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::decimal-literal-representation` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::decimal_literal_representation)]`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
1
tests/ui-toml/disallowed_script_idents/clippy.toml
Normal file
1
tests/ui-toml/disallowed_script_idents/clippy.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
allowed-scripts = ["Cyrillic"]
|
@ -0,0 +1,6 @@
|
|||||||
|
#![warn(clippy::disallowed_script_idents)]
|
||||||
|
fn main() {
|
||||||
|
let счётчик = 10;
|
||||||
|
let カウンタ = 10;
|
||||||
|
//~^ ERROR: identifier `カウンタ` has a Unicode script that is not allowed by configuration
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
error: identifier `カウンタ` has a Unicode script that is not allowed by configuration: Katakana
|
||||||
|
--> $DIR/disallowed_script_idents.rs:4:9
|
||||||
|
|
|
||||||
|
LL | let カウンタ = 10;
|
||||||
|
| ^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `-D clippy::disallowed-script-idents` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::disallowed_script_idents)]`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
1
tests/ui-toml/enum_variant_names/clippy.toml
Normal file
1
tests/ui-toml/enum_variant_names/clippy.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
enum-variant-name-threshold = 5
|
16
tests/ui-toml/enum_variant_names/enum_variant_names.rs
Normal file
16
tests/ui-toml/enum_variant_names/enum_variant_names.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
enum Foo {
|
||||||
|
AFoo,
|
||||||
|
BFoo,
|
||||||
|
CFoo,
|
||||||
|
DFoo,
|
||||||
|
}
|
||||||
|
enum Foo2 {
|
||||||
|
//~^ ERROR: all variants have the same postfix
|
||||||
|
AFoo,
|
||||||
|
BFoo,
|
||||||
|
CFoo,
|
||||||
|
DFoo,
|
||||||
|
EFoo,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
18
tests/ui-toml/enum_variant_names/enum_variant_names.stderr
Normal file
18
tests/ui-toml/enum_variant_names/enum_variant_names.stderr
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
error: all variants have the same postfix: `Foo`
|
||||||
|
--> $DIR/enum_variant_names.rs:7:1
|
||||||
|
|
|
||||||
|
LL | / enum Foo2 {
|
||||||
|
LL | |
|
||||||
|
LL | | AFoo,
|
||||||
|
LL | | BFoo,
|
||||||
|
... |
|
||||||
|
LL | | EFoo,
|
||||||
|
LL | | }
|
||||||
|
| |_^
|
||||||
|
|
|
||||||
|
= help: remove the postfixes and use full paths to the variants instead of glob imports
|
||||||
|
= note: `-D clippy::enum-variant-names` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::enum_variant_names)]`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
1
tests/ui-toml/enum_variant_size/clippy.toml
Normal file
1
tests/ui-toml/enum_variant_size/clippy.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
enum-variant-size-threshold = 500
|
11
tests/ui-toml/enum_variant_size/enum_variant_size.fixed
Normal file
11
tests/ui-toml/enum_variant_size/enum_variant_size.fixed
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
enum Fine {
|
||||||
|
A(()),
|
||||||
|
B([u8; 500]),
|
||||||
|
}
|
||||||
|
enum Bad {
|
||||||
|
//~^ ERROR: large size difference between variants
|
||||||
|
A(()),
|
||||||
|
B(Box<[u8; 501]>),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
11
tests/ui-toml/enum_variant_size/enum_variant_size.rs
Normal file
11
tests/ui-toml/enum_variant_size/enum_variant_size.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
enum Fine {
|
||||||
|
A(()),
|
||||||
|
B([u8; 500]),
|
||||||
|
}
|
||||||
|
enum Bad {
|
||||||
|
//~^ ERROR: large size difference between variants
|
||||||
|
A(()),
|
||||||
|
B([u8; 501]),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
21
tests/ui-toml/enum_variant_size/enum_variant_size.stderr
Normal file
21
tests/ui-toml/enum_variant_size/enum_variant_size.stderr
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
error: large size difference between variants
|
||||||
|
--> $DIR/enum_variant_size.rs:5:1
|
||||||
|
|
|
||||||
|
LL | / enum Bad {
|
||||||
|
LL | |
|
||||||
|
LL | | A(()),
|
||||||
|
| | ----- the second-largest variant contains at least 0 bytes
|
||||||
|
LL | | B([u8; 501]),
|
||||||
|
| | ------------ the largest variant contains at least 501 bytes
|
||||||
|
LL | | }
|
||||||
|
| |_^ the entire enum is at least 502 bytes
|
||||||
|
|
|
||||||
|
= note: `-D clippy::large-enum-variant` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]`
|
||||||
|
help: consider boxing the large fields to reduce the total size of the enum
|
||||||
|
|
|
||||||
|
LL | B(Box<[u8; 501]>),
|
||||||
|
| ~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
1
tests/ui-toml/enum_variants_threshold0/clippy.toml
Normal file
1
tests/ui-toml/enum_variants_threshold0/clippy.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
enum-variant-name-threshold = 0
|
@ -0,0 +1,3 @@
|
|||||||
|
enum Actions {}
|
||||||
|
|
||||||
|
fn main() {}
|
1
tests/ui-toml/explicit_iter_loop/clippy.toml
Normal file
1
tests/ui-toml/explicit_iter_loop/clippy.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
enforce-iter-loop-reborrow = true
|
10
tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed
Normal file
10
tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#![warn(clippy::explicit_iter_loop)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut vec = vec![1, 2, 3];
|
||||||
|
let rmvec = &mut vec;
|
||||||
|
for _ in &*rmvec {}
|
||||||
|
//~^ ERROR: it is more concise to loop over references to containers
|
||||||
|
for _ in &mut *rmvec {}
|
||||||
|
//~^ ERROR: it is more concise to loop over references to containers
|
||||||
|
}
|
10
tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs
Normal file
10
tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#![warn(clippy::explicit_iter_loop)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut vec = vec![1, 2, 3];
|
||||||
|
let rmvec = &mut vec;
|
||||||
|
for _ in rmvec.iter() {}
|
||||||
|
//~^ ERROR: it is more concise to loop over references to containers
|
||||||
|
for _ in rmvec.iter_mut() {}
|
||||||
|
//~^ ERROR: it is more concise to loop over references to containers
|
||||||
|
}
|
17
tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr
Normal file
17
tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
error: it is more concise to loop over references to containers instead of using explicit iteration methods
|
||||||
|
--> $DIR/explicit_iter_loop.rs:6:14
|
||||||
|
|
|
||||||
|
LL | for _ in rmvec.iter() {}
|
||||||
|
| ^^^^^^^^^^^^ help: to write this more concisely, try: `&*rmvec`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::explicit-iter-loop` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::explicit_iter_loop)]`
|
||||||
|
|
||||||
|
error: it is more concise to loop over references to containers instead of using explicit iteration methods
|
||||||
|
--> $DIR/explicit_iter_loop.rs:8:14
|
||||||
|
|
|
||||||
|
LL | for _ in rmvec.iter_mut() {}
|
||||||
|
| ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *rmvec`
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
1
tests/ui-toml/large_stack_frames/clippy.toml
Normal file
1
tests/ui-toml/large_stack_frames/clippy.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
stack-size-threshold = 1000
|
17
tests/ui-toml/large_stack_frames/large_stack_frames.rs
Normal file
17
tests/ui-toml/large_stack_frames/large_stack_frames.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#![warn(clippy::large_stack_frames)]
|
||||||
|
|
||||||
|
// We use this helper function instead of writing [0; 4294967297] directly to represent a
|
||||||
|
// case that large_stack_arrays can't catch
|
||||||
|
fn create_array<const N: usize>() -> [u8; N] {
|
||||||
|
[0; N]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let _x = create_array::<1000>();
|
||||||
|
}
|
||||||
|
fn f2() {
|
||||||
|
//~^ ERROR: this function allocates a large amount of stack space
|
||||||
|
let _x = create_array::<1001>();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
15
tests/ui-toml/large_stack_frames/large_stack_frames.stderr
Normal file
15
tests/ui-toml/large_stack_frames/large_stack_frames.stderr
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
error: this function allocates a large amount of stack space
|
||||||
|
--> $DIR/large_stack_frames.rs:12:1
|
||||||
|
|
|
||||||
|
LL | / fn f2() {
|
||||||
|
LL | |
|
||||||
|
LL | | let _x = create_array::<1001>();
|
||||||
|
LL | | }
|
||||||
|
| |_^
|
||||||
|
|
|
||||||
|
= note: allocating large amounts of stack space can overflow the stack
|
||||||
|
= note: `-D clippy::large-stack-frames` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::large_stack_frames)]`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
1
tests/ui-toml/large_types_passed_by_value/clippy.toml
Normal file
1
tests/ui-toml/large_types_passed_by_value/clippy.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
pass-by-value-size-limit = 512
|
@ -0,0 +1,7 @@
|
|||||||
|
#![warn(clippy::large_types_passed_by_value)]
|
||||||
|
|
||||||
|
fn f(_v: [u8; 512]) {}
|
||||||
|
fn f2(_v: &[u8; 513]) {}
|
||||||
|
//~^ ERROR: this argument (513 byte) is passed by value
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,7 @@
|
|||||||
|
#![warn(clippy::large_types_passed_by_value)]
|
||||||
|
|
||||||
|
fn f(_v: [u8; 512]) {}
|
||||||
|
fn f2(_v: [u8; 513]) {}
|
||||||
|
//~^ ERROR: this argument (513 byte) is passed by value
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,11 @@
|
|||||||
|
error: this argument (513 byte) is passed by value, but might be more efficient if passed by reference (limit: 512 byte)
|
||||||
|
--> $DIR/large_types_passed_by_value.rs:4:11
|
||||||
|
|
|
||||||
|
LL | fn f2(_v: [u8; 513]) {}
|
||||||
|
| ^^^^^^^^^ help: consider passing by reference instead: `&[u8; 513]`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::large-types-passed-by-value` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::large_types_passed_by_value)]`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
1
tests/ui-toml/manual_let_else/clippy.toml
Normal file
1
tests/ui-toml/manual_let_else/clippy.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
matches-for-let-else = "AllTypes"
|
10
tests/ui-toml/manual_let_else/manual_let_else.fixed
Normal file
10
tests/ui-toml/manual_let_else/manual_let_else.fixed
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#![warn(clippy::manual_let_else)]
|
||||||
|
|
||||||
|
enum Foo {
|
||||||
|
A(u8),
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let Foo::A(x) = Foo::A(1) else { return };
|
||||||
|
}
|
14
tests/ui-toml/manual_let_else/manual_let_else.rs
Normal file
14
tests/ui-toml/manual_let_else/manual_let_else.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#![warn(clippy::manual_let_else)]
|
||||||
|
|
||||||
|
enum Foo {
|
||||||
|
A(u8),
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = match Foo::A(1) {
|
||||||
|
//~^ ERROR: this could be rewritten as `let...else`
|
||||||
|
Foo::A(x) => x,
|
||||||
|
Foo::B => return,
|
||||||
|
};
|
||||||
|
}
|
15
tests/ui-toml/manual_let_else/manual_let_else.stderr
Normal file
15
tests/ui-toml/manual_let_else/manual_let_else.stderr
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
error: this could be rewritten as `let...else`
|
||||||
|
--> $DIR/manual_let_else.rs:9:5
|
||||||
|
|
|
||||||
|
LL | / let x = match Foo::A(1) {
|
||||||
|
LL | |
|
||||||
|
LL | | Foo::A(x) => x,
|
||||||
|
LL | | Foo::B => return,
|
||||||
|
LL | | };
|
||||||
|
| |______^ help: consider writing: `let Foo::A(x) = Foo::A(1) else { return };`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::manual-let-else` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::manual_let_else)]`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
1
tests/ui-toml/path_ends_with_ext/clippy.toml
Normal file
1
tests/ui-toml/path_ends_with_ext/clippy.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
allowed-dotfiles = ["dot"]
|
9
tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs
Normal file
9
tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#![warn(clippy::path_ends_with_ext)]
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn f(p: &Path) {
|
||||||
|
p.ends_with(".dot");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user