mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-19 11:12:43 +00:00
Merge commit '9d6f41691ed9dbfaec2a2df2661c42451f2fe0d3' into clippy-subtree-update
This commit is contained in:
parent
bc0965e2ff
commit
0e62b18435
10
.github/workflows/clippy.yml
vendored
10
.github/workflows/clippy.yml
vendored
@ -26,6 +26,12 @@ env:
|
||||
NO_FMT_TEST: 1
|
||||
CARGO_INCREMENTAL: 0
|
||||
|
||||
concurrency:
|
||||
# For a given workflow, if we push to the same PR, cancel all previous builds on that PR.
|
||||
# If the push is not attached to a PR, we will cancel all builds on the same branch.
|
||||
group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
base:
|
||||
# NOTE: If you modify this job, make sure you copy the changes to clippy_bors.yml
|
||||
@ -33,10 +39,6 @@ jobs:
|
||||
|
||||
steps:
|
||||
# Setup
|
||||
- uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
|
25
.github/workflows/clippy_bors.yml
vendored
25
.github/workflows/clippy_bors.yml
vendored
@ -12,6 +12,11 @@ env:
|
||||
NO_FMT_TEST: 1
|
||||
CARGO_INCREMENTAL: 0
|
||||
|
||||
concurrency:
|
||||
# For a given workflow, if we push to the same branch, cancel all previous builds on that branch.
|
||||
group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@ -21,10 +26,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@ -67,10 +68,6 @@ jobs:
|
||||
# NOTE: If you modify this job, make sure you copy the changes to clippy.yml
|
||||
steps:
|
||||
# Setup
|
||||
- uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@ -131,10 +128,6 @@ jobs:
|
||||
|
||||
steps:
|
||||
# Setup
|
||||
- uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@ -155,10 +148,6 @@ jobs:
|
||||
|
||||
steps:
|
||||
# Setup
|
||||
- uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@ -211,10 +200,6 @@ jobs:
|
||||
|
||||
steps:
|
||||
# Setup
|
||||
- uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
|
63
CHANGELOG.md
63
CHANGELOG.md
@ -6,11 +6,65 @@ document.
|
||||
|
||||
## Unreleased / Beta / In Rust Nightly
|
||||
|
||||
[a859e5cc...master](https://github.com/rust-lang/rust-clippy/compare/a859e5cc...master)
|
||||
[66c29b97...master](https://github.com/rust-lang/rust-clippy/compare/66c29b97...master)
|
||||
|
||||
## Rust 1.77
|
||||
|
||||
Current stable, released 2024-03-18
|
||||
|
||||
[View all 93 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-12-16T18%3A20%3A00Z..2024-01-25T18%3A15%3A56Z+base%3Amaster)
|
||||
|
||||
### New Lints
|
||||
|
||||
* [`suspicious_open_options`]
|
||||
[#11608](https://github.com/rust-lang/rust-clippy/pull/11608)
|
||||
* [`option_as_ref_cloned`]
|
||||
[#12051](https://github.com/rust-lang/rust-clippy/pull/12051)
|
||||
* [`thread_local_initializer_can_be_made_const`]
|
||||
[#12026](https://github.com/rust-lang/rust-clippy/pull/12026)
|
||||
* [`str_split_at_newline`]
|
||||
[#11987](https://github.com/rust-lang/rust-clippy/pull/11987)
|
||||
* [`empty_enum_variants_with_brackets`]
|
||||
[#12047](https://github.com/rust-lang/rust-clippy/pull/12047)
|
||||
* [`manual_is_variant_and`]
|
||||
[#11865](https://github.com/rust-lang/rust-clippy/pull/11865)
|
||||
* [`pub_underscore_fields`]
|
||||
[#10283](https://github.com/rust-lang/rust-clippy/pull/10283)
|
||||
* [`eager_transmute`]
|
||||
[#11981](https://github.com/rust-lang/rust-clippy/pull/11981)
|
||||
* [`iter_filter_is_some`]
|
||||
[#12004](https://github.com/rust-lang/rust/pull/12004)
|
||||
* [`iter_filter_is_ok`]
|
||||
[#12004](https://github.com/rust-lang/rust/pull/12004)
|
||||
* [`result_filter_map`]
|
||||
[#11869](https://github.com/rust-lang/rust-clippy/pull/11869)
|
||||
* [`unconditional_recursion`]
|
||||
[#11938](https://github.com/rust-lang/rust-clippy/pull/11938)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [`multiple_crate_versions`]: Added the [`allowed-duplicate-crates`] configuration to allow specific crates
|
||||
[#12179](https://github.com/rust-lang/rust-clippy/pull/12179)
|
||||
* [`single_call_fn`]: No longer ignores `#[allow]` attributes
|
||||
[#12183](https://github.com/rust-lang/rust-clippy/pull/12183)
|
||||
* [`read_zero_byte_vec`]: Updated the heuristics used for linting
|
||||
[#11766](https://github.com/rust-lang/rust-clippy/pull/11766)
|
||||
|
||||
### ICE Fixes
|
||||
|
||||
* [`unit_arg`]: No longer crashes when checking for const in nested bodies
|
||||
[#11977](https://github.com/rust-lang/rust-clippy/pull/11977)
|
||||
* [`indexing_slicing`]: No longer crashes when the array index exceeds `usize`
|
||||
[#12266](https://github.com/rust-lang/rust-clippy/pull/12266)
|
||||
|
||||
### Others
|
||||
|
||||
* Warnings about invalid fields inside `clippy.toml` files now include suggestions for existing fields
|
||||
[#12180](https://github.com/rust-lang/rust-clippy/pull/12180)
|
||||
|
||||
## Rust 1.76
|
||||
|
||||
Current stable, released 2024-02-08
|
||||
Released 2024-02-08
|
||||
|
||||
[View all 85 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-11-02T20%3A23%3A40Z..2023-12-16T13%3A11%3A08Z+base%3Amaster)
|
||||
|
||||
@ -5110,6 +5164,7 @@ Released 2018-09-13
|
||||
[`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read
|
||||
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
|
||||
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
|
||||
[`const_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_is_empty
|
||||
[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
|
||||
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
|
||||
[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
|
||||
@ -5156,6 +5211,7 @@ Released 2018-09-13
|
||||
[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
|
||||
[`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod
|
||||
[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
|
||||
[`duplicated_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicated_attributes
|
||||
[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
|
||||
[`eager_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#eager_transmute
|
||||
[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
|
||||
@ -5279,6 +5335,7 @@ Released 2018-09-13
|
||||
[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
|
||||
[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
|
||||
[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
|
||||
[`integer_division_remainder_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division_remainder_used
|
||||
[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
|
||||
[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
|
||||
[`into_iter_without_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_without_iter
|
||||
@ -5376,6 +5433,7 @@ Released 2018-09-13
|
||||
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
|
||||
[`manual_try_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold
|
||||
[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
|
||||
[`manual_unwrap_or_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default
|
||||
[`manual_while_let_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_while_let_some
|
||||
[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
|
||||
[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
|
||||
@ -5813,6 +5871,7 @@ Released 2018-09-13
|
||||
[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
|
||||
[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
|
||||
[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
|
||||
[`zero_repeat_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_repeat_side_effects
|
||||
[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
|
||||
[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
|
||||
[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
|
||||
|
12
Cargo.toml
12
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy"
|
||||
version = "0.1.78"
|
||||
version = "0.1.79"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
@ -24,7 +24,7 @@ path = "src/driver.rs"
|
||||
clippy_config = { path = "clippy_config" }
|
||||
clippy_lints = { path = "clippy_lints" }
|
||||
rustc_tools_util = "0.3.0"
|
||||
tempfile = { version = "3.2", optional = true }
|
||||
tempfile = { version = "3.3", optional = true }
|
||||
termize = "0.1"
|
||||
color-print = "0.3.4"
|
||||
anstream = "0.6.0"
|
||||
@ -32,18 +32,18 @@ anstream = "0.6.0"
|
||||
[dev-dependencies]
|
||||
ui_test = "0.22.2"
|
||||
tester = "0.9"
|
||||
regex = "1.5"
|
||||
regex = "1.5.5"
|
||||
toml = "0.7.3"
|
||||
walkdir = "2.3"
|
||||
# This is used by the `collect-metadata` alias.
|
||||
filetime = "0.2"
|
||||
filetime = "0.2.9"
|
||||
itertools = "0.12"
|
||||
|
||||
# UI test dependencies
|
||||
clippy_utils = { path = "clippy_utils" }
|
||||
if_chain = "1.0"
|
||||
quote = "1.0"
|
||||
serde = { version = "1.0.125", features = ["derive"] }
|
||||
quote = "1.0.25"
|
||||
serde = { version = "1.0.145", features = ["derive"] }
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
futures = "0.3"
|
||||
parking_lot = "0.12"
|
||||
|
@ -52,7 +52,7 @@ if expr.span.from_expansion() {
|
||||
|
||||
### `Span.ctxt` method
|
||||
|
||||
The `span`'s context, given by the method [`ctxt`] and returning [SpanContext],
|
||||
The `span`'s context, given by the method [`ctxt`] and returning [SyntaxContext],
|
||||
represents if the span is from a macro expansion and, if it is, which
|
||||
macro call expanded this span.
|
||||
|
||||
@ -155,4 +155,4 @@ if in_external_macro(cx.sess(), foo_span) {
|
||||
[`from_expansion`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
|
||||
[`in_external_macro`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
|
||||
[Span]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html
|
||||
[SpanContext]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/hygiene/struct.SyntaxContext.html
|
||||
[SyntaxContext]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/hygiene/struct.SyntaxContext.html
|
||||
|
@ -602,6 +602,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
|
||||
**Affected lints:**
|
||||
* [`almost_complete_range`](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range)
|
||||
* [`approx_constant`](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant)
|
||||
* [`assigning_clones`](https://rust-lang.github.io/rust-clippy/master/index.html#assigning_clones)
|
||||
* [`borrow_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr)
|
||||
* [`cast_abs_to_unsigned`](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned)
|
||||
* [`checked_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions)
|
||||
|
13
clippy.toml
13
clippy.toml
@ -1,7 +1,10 @@
|
||||
avoid-breaking-exported-api = false
|
||||
|
||||
# use the various `span_lint_*` methods instead, which also add a link to the docs
|
||||
disallowed-methods = [
|
||||
"rustc_lint::context::LintContext::span_lint",
|
||||
"rustc_middle::ty::context::TyCtxt::node_span_lint"
|
||||
]
|
||||
[[disallowed-methods]]
|
||||
path = "rustc_lint::context::LintContext::span_lint"
|
||||
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
|
||||
|
||||
|
||||
[[disallowed-methods]]
|
||||
path = "rustc_middle::ty::context::TyCtxt::node_span_lint"
|
||||
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy_config"
|
||||
version = "0.1.78"
|
||||
version = "0.1.79"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
@ -262,7 +262,7 @@ define_Conf! {
|
||||
///
|
||||
/// Suppress lints whenever the suggested change would cause breakage for other crates.
|
||||
(avoid_breaking_exported_api: bool = true),
|
||||
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS.
|
||||
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES.
|
||||
///
|
||||
/// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
|
||||
#[default_text = ""]
|
||||
|
@ -23,6 +23,7 @@ msrv_aliases! {
|
||||
1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN }
|
||||
1,68,0 { PATH_MAIN_SEPARATOR_STR }
|
||||
1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
|
||||
1,63,0 { ASSIGNING_CLONES }
|
||||
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
|
||||
1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST }
|
||||
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }
|
||||
|
@ -689,6 +689,8 @@ fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String {
|
||||
fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String {
|
||||
let mut seen_lints = HashSet::new();
|
||||
let mut res: String = GENERATED_FILE_COMMENT.into();
|
||||
|
||||
res.push_str("#![allow(clippy::duplicated_attributes)]\n");
|
||||
for lint in lints {
|
||||
if seen_lints.insert(&lint.new_name) {
|
||||
writeln!(res, "#![allow({})]", lint.new_name).unwrap();
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.78"
|
||||
version = "0.1.79"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
@ -1,3 +1,4 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::HirNode;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
@ -6,7 +7,7 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::{self as hir, Expr, ExprKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Instance, Mutability};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::ExpnKind;
|
||||
@ -49,7 +50,19 @@ declare_clippy_lint! {
|
||||
perf,
|
||||
"assigning the result of cloning may be inefficient"
|
||||
}
|
||||
declare_lint_pass!(AssigningClones => [ASSIGNING_CLONES]);
|
||||
|
||||
pub struct AssigningClones {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl AssigningClones {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for AssigningClones {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx hir::Expr<'_>) {
|
||||
@ -68,10 +81,12 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
|
||||
return;
|
||||
};
|
||||
|
||||
if is_ok_to_suggest(cx, lhs, &call) {
|
||||
if is_ok_to_suggest(cx, lhs, &call, &self.msrv) {
|
||||
suggest(cx, assign_expr, lhs, &call);
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
// Try to resolve the call to `Clone::clone` or `ToOwned::to_owned`.
|
||||
@ -135,7 +150,13 @@ fn extract_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<
|
||||
|
||||
// Return true if we find that the called method has a custom implementation and isn't derived or
|
||||
// provided by default by the corresponding trait.
|
||||
fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>) -> bool {
|
||||
fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>, msrv: &Msrv) -> bool {
|
||||
// For calls to .to_owned we suggest using .clone_into(), which was only stablilized in 1.63.
|
||||
// If the current MSRV is below that, don't suggest the lint.
|
||||
if !msrv.meets(msrvs::ASSIGNING_CLONES) && matches!(call.target, TargetTrait::ToOwned) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the left-hand side is a local variable, it might be uninitialized at this point.
|
||||
// In that case we do not want to suggest the lint.
|
||||
if let Some(local) = path_to_local(lhs) {
|
||||
|
64
clippy_lints/src/attrs/duplicated_attributes.rs
Normal file
64
clippy_lints/src/attrs/duplicated_attributes.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use super::DUPLICATED_ATTRIBUTES;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_ast::{Attribute, MetaItem};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_lint::EarlyContext;
|
||||
use rustc_span::{sym, Span};
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
fn emit_if_duplicated(
|
||||
cx: &EarlyContext<'_>,
|
||||
attr: &MetaItem,
|
||||
attr_paths: &mut FxHashMap<String, Span>,
|
||||
complete_path: String,
|
||||
) {
|
||||
match attr_paths.entry(complete_path) {
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(attr.span);
|
||||
},
|
||||
Entry::Occupied(o) => {
|
||||
span_lint_and_then(cx, DUPLICATED_ATTRIBUTES, attr.span, "duplicated attribute", |diag| {
|
||||
diag.span_note(*o.get(), "first defined here");
|
||||
diag.span_help(attr.span, "remove this attribute");
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn check_duplicated_attr(
|
||||
cx: &EarlyContext<'_>,
|
||||
attr: &MetaItem,
|
||||
attr_paths: &mut FxHashMap<String, Span>,
|
||||
parent: &mut Vec<String>,
|
||||
) {
|
||||
let Some(ident) = attr.ident() else { return };
|
||||
let name = ident.name;
|
||||
if name == sym::doc || name == sym::cfg_attr {
|
||||
// FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
|
||||
// conditions are the same.
|
||||
return;
|
||||
}
|
||||
if let Some(value) = attr.value_str() {
|
||||
emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}={value}", parent.join(":")));
|
||||
} else if let Some(sub_attrs) = attr.meta_item_list() {
|
||||
parent.push(name.as_str().to_string());
|
||||
for sub_attr in sub_attrs {
|
||||
if let Some(meta) = sub_attr.meta_item() {
|
||||
check_duplicated_attr(cx, meta, attr_paths, parent);
|
||||
}
|
||||
}
|
||||
parent.pop();
|
||||
} else {
|
||||
emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}", parent.join(":")));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) {
|
||||
let mut attr_paths = FxHashMap::default();
|
||||
|
||||
for attr in attrs {
|
||||
if let Some(meta) = attr.meta() {
|
||||
check_duplicated_attr(cx, &meta, &mut attr_paths, &mut Vec::new());
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ mod allow_attributes_without_reason;
|
||||
mod blanket_clippy_restriction_lints;
|
||||
mod deprecated_cfg_attr;
|
||||
mod deprecated_semver;
|
||||
mod duplicated_attributes;
|
||||
mod empty_line_after;
|
||||
mod inline_always;
|
||||
mod maybe_misused_cfg;
|
||||
@ -16,7 +17,7 @@ mod useless_attribute;
|
||||
mod utils;
|
||||
|
||||
use clippy_config::msrvs::Msrv;
|
||||
use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem};
|
||||
use rustc_ast::{Attribute, Crate, MetaItemKind, NestedMetaItem};
|
||||
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, impl_lint_pass};
|
||||
@ -489,6 +490,32 @@ declare_clippy_lint! {
|
||||
"item has both inner and outer attributes"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for attributes that appear two or more times.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Repeating an attribute on the same item (or globally on the same crate)
|
||||
/// is unnecessary and doesn't have an effect.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[allow(dead_code)]
|
||||
/// #[allow(dead_code)]
|
||||
/// fn foo() {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[allow(dead_code)]
|
||||
/// fn foo() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub DUPLICATED_ATTRIBUTES,
|
||||
suspicious,
|
||||
"duplicated attribute"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Attributes => [
|
||||
ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
INLINE_ALWAYS,
|
||||
@ -568,12 +595,18 @@ impl_lint_pass!(EarlyAttributes => [
|
||||
DEPRECATED_CLIPPY_CFG_ATTR,
|
||||
UNNECESSARY_CLIPPY_CFG,
|
||||
MIXED_ATTRIBUTES_STYLE,
|
||||
DUPLICATED_ATTRIBUTES,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for EarlyAttributes {
|
||||
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
|
||||
duplicated_attributes::check(cx, &krate.attrs);
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
|
||||
empty_line_after::check(cx, item);
|
||||
mixed_attributes_style::check(cx, item);
|
||||
duplicated_attributes::check(cx, &item.attrs);
|
||||
}
|
||||
|
||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
|
@ -1,12 +1,12 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::in_constant;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
|
||||
use clippy_utils::ty::is_isize_or_usize;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_hir::{Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, FloatTy, Ty};
|
||||
use rustc_middle::ty::{self, FloatTy, Ty, UintTy};
|
||||
|
||||
use super::{utils, CAST_LOSSLESS};
|
||||
|
||||
@ -16,6 +16,7 @@ pub(super) fn check(
|
||||
cast_op: &Expr<'_>,
|
||||
cast_from: Ty<'_>,
|
||||
cast_to: Ty<'_>,
|
||||
cast_to_hir: &rustc_hir::Ty<'_>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if !should_lint(cx, expr, cast_from, cast_to, msrv) {
|
||||
@ -24,11 +25,11 @@ pub(super) fn check(
|
||||
|
||||
// The suggestion is to use a function call, so if the original expression
|
||||
// has parens on the outside, they are no longer needed.
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let opt = snippet_opt(cx, cast_op.span.source_callsite());
|
||||
let sugg = opt.as_ref().map_or_else(
|
||||
|| {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
app = Applicability::HasPlaceholders;
|
||||
".."
|
||||
},
|
||||
|snip| {
|
||||
@ -40,10 +41,27 @@ pub(super) fn check(
|
||||
},
|
||||
);
|
||||
|
||||
let message = if cast_from.is_bool() {
|
||||
format!("casting `{cast_from:}` to `{cast_to:}` is more cleanly stated with `{cast_to:}::from(_)`")
|
||||
// Display the type alias instead of the aliased type. Fixes #11285
|
||||
//
|
||||
// FIXME: Once `lazy_type_alias` is stabilized(?) we should use `rustc_middle` types instead,
|
||||
// this will allow us to display the right type with `cast_from` as well.
|
||||
let cast_to_fmt = if let TyKind::Path(QPath::Resolved(None, path)) = cast_to_hir.kind
|
||||
// It's a bit annoying but the turbofish is optional for types. A type in an `as` cast
|
||||
// shouldn't have these if they're primitives, which are the only things we deal with.
|
||||
//
|
||||
// This could be removed for performance if this check is determined to have a pretty major
|
||||
// effect.
|
||||
&& path.segments.iter().all(|segment| segment.args.is_none())
|
||||
{
|
||||
snippet_with_applicability(cx, cast_to_hir.span, "..", &mut app)
|
||||
} else {
|
||||
format!("casting `{cast_from}` to `{cast_to}` may become silently lossy if you later change the type")
|
||||
cast_to.to_string().into()
|
||||
};
|
||||
|
||||
let message = if cast_from.is_bool() {
|
||||
format!("casting `{cast_from}` to `{cast_to_fmt}` is more cleanly stated with `{cast_to_fmt}::from(_)`")
|
||||
} else {
|
||||
format!("casting `{cast_from}` to `{cast_to_fmt}` may become silently lossy if you later change the type")
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
@ -52,14 +70,17 @@ pub(super) fn check(
|
||||
expr.span,
|
||||
&message,
|
||||
"try",
|
||||
format!("{cast_to}::from({sugg})"),
|
||||
applicability,
|
||||
format!("{cast_to_fmt}::from({sugg})"),
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
||||
fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool {
|
||||
// Do not suggest using From in consts/statics until it is valid to do so (see #2267).
|
||||
if in_constant(cx, expr.hir_id) {
|
||||
//
|
||||
// If destination is u128, do not lint because source type cannot be larger
|
||||
// If source is bool, still lint due to the lint message differing (refers to style)
|
||||
if in_constant(cx, expr.hir_id) || (!cast_from.is_bool() && matches!(cast_to.kind(), ty::Uint(UintTy::U128))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -791,7 +791,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
|
||||
cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
}
|
||||
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
|
||||
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir, &self.msrv);
|
||||
cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,14 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
use clippy_utils::macros::{macro_backtrace, MacroCall};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{is_in_cfg_test, is_in_test_function};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_hir::{Expr, ExprKind, HirId, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::{sym, Span, SyntaxContext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -31,31 +33,38 @@ declare_clippy_lint! {
|
||||
"`dbg!` macro is intended as a debugging tool"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct DbgMacro {
|
||||
allow_dbg_in_tests: bool,
|
||||
/// Tracks the `dbg!` macro callsites that are already checked.
|
||||
checked_dbg_call_site: FxHashSet<Span>,
|
||||
/// Tracks the previous `SyntaxContext`, to avoid walking the same context chain.
|
||||
prev_ctxt: SyntaxContext,
|
||||
}
|
||||
|
||||
impl_lint_pass!(DbgMacro => [DBG_MACRO]);
|
||||
|
||||
impl DbgMacro {
|
||||
pub fn new(allow_dbg_in_tests: bool) -> Self {
|
||||
DbgMacro { allow_dbg_in_tests }
|
||||
DbgMacro {
|
||||
allow_dbg_in_tests,
|
||||
checked_dbg_call_site: FxHashSet::default(),
|
||||
prev_ctxt: SyntaxContext::root(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for DbgMacro {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
|
||||
return;
|
||||
};
|
||||
if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) {
|
||||
let cur_syntax_ctxt = expr.span.ctxt();
|
||||
|
||||
if cur_syntax_ctxt != self.prev_ctxt &&
|
||||
let Some(macro_call) = first_dbg_macro_in_expansion(cx, expr.span) &&
|
||||
!in_external_macro(cx.sess(), macro_call.span) &&
|
||||
self.checked_dbg_call_site.insert(macro_call.span) &&
|
||||
// allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
|
||||
if self.allow_dbg_in_tests
|
||||
&& (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
!(self.allow_dbg_in_tests && is_in_test(cx, expr.hir_id))
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
let (sugg_span, suggestion) = match expr.peel_drop_temps().kind {
|
||||
@ -101,6 +110,8 @@ impl LateLintPass<'_> for DbgMacro {
|
||||
_ => return,
|
||||
};
|
||||
|
||||
self.prev_ctxt = cur_syntax_ctxt;
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DBG_MACRO,
|
||||
@ -112,4 +123,16 @@ impl LateLintPass<'_> for DbgMacro {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_crate_post(&mut self, _: &LateContext<'_>) {
|
||||
self.checked_dbg_call_site = FxHashSet::default();
|
||||
}
|
||||
}
|
||||
|
||||
fn is_in_test(cx: &LateContext<'_>, hir_id: HirId) -> bool {
|
||||
is_in_test_function(cx.tcx, hir_id) || is_in_cfg_test(cx.tcx, hir_id)
|
||||
}
|
||||
|
||||
fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option<MacroCall> {
|
||||
macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id))
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::attrs::DEPRECATED_CFG_ATTR_INFO,
|
||||
crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO,
|
||||
crate::attrs::DEPRECATED_SEMVER_INFO,
|
||||
crate::attrs::DUPLICATED_ATTRIBUTES_INFO,
|
||||
crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
|
||||
crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
|
||||
crate::attrs::INLINE_ALWAYS_INFO,
|
||||
@ -235,6 +236,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO,
|
||||
crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO,
|
||||
crate::int_plus_one::INT_PLUS_ONE_INFO,
|
||||
crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO,
|
||||
crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO,
|
||||
crate::item_name_repetitions::ENUM_VARIANT_NAMES_INFO,
|
||||
crate::item_name_repetitions::MODULE_INCEPTION_INFO,
|
||||
@ -310,6 +312,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO,
|
||||
crate::manual_string_new::MANUAL_STRING_NEW_INFO,
|
||||
crate::manual_strip::MANUAL_STRIP_INFO,
|
||||
crate::manual_unwrap_or_default::MANUAL_UNWRAP_OR_DEFAULT_INFO,
|
||||
crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO,
|
||||
crate::map_unit_fn::RESULT_MAP_UNIT_FN_INFO,
|
||||
crate::match_result_ok::MATCH_RESULT_OK_INFO,
|
||||
@ -353,6 +356,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::methods::CLONE_ON_COPY_INFO,
|
||||
crate::methods::CLONE_ON_REF_PTR_INFO,
|
||||
crate::methods::COLLAPSIBLE_STR_REPLACE_INFO,
|
||||
crate::methods::CONST_IS_EMPTY_INFO,
|
||||
crate::methods::DRAIN_COLLECT_INFO,
|
||||
crate::methods::ERR_EXPECT_INFO,
|
||||
crate::methods::EXPECT_FUN_CALL_INFO,
|
||||
@ -750,5 +754,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||
crate::write::WRITE_LITERAL_INFO,
|
||||
crate::write::WRITE_WITH_NEWLINE_INFO,
|
||||
crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO,
|
||||
crate::zero_repeat_side_effects::ZERO_REPEAT_SIDE_EFFECTS_INFO,
|
||||
crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO,
|
||||
];
|
||||
|
@ -8,7 +8,14 @@ use url::Url;
|
||||
|
||||
use crate::doc::DOC_MARKDOWN;
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span, code_level: isize) {
|
||||
pub fn check(
|
||||
cx: &LateContext<'_>,
|
||||
valid_idents: &FxHashSet<String>,
|
||||
text: &str,
|
||||
span: Span,
|
||||
code_level: isize,
|
||||
blockquote_level: isize,
|
||||
) {
|
||||
for orig_word in text.split(|c: char| c.is_whitespace() || c == '\'') {
|
||||
// Trim punctuation as in `some comment (see foo::bar).`
|
||||
// ^^
|
||||
@ -46,11 +53,11 @@ pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str,
|
||||
span.parent(),
|
||||
);
|
||||
|
||||
check_word(cx, word, span, code_level);
|
||||
check_word(cx, word, span, code_level, blockquote_level);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize) {
|
||||
fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, blockquote_level: isize) {
|
||||
/// Checks if a string is upper-camel-case, i.e., starts with an uppercase and
|
||||
/// contains at least two uppercase letters (`Clippy` is ok) and one lower-case
|
||||
/// letter (`NASA` is ok).
|
||||
@ -97,7 +104,9 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize) {
|
||||
}
|
||||
|
||||
// We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343)
|
||||
if code_level > 0 || (has_underscore(word) && has_hyphen(word)) {
|
||||
//
|
||||
// We also assume that backticks are not necessary if inside a quote. (Issue #10262)
|
||||
if code_level > 0 || blockquote_level > 0 || (has_underscore(word) && has_hyphen(word)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -7,14 +7,14 @@ use clippy_utils::{is_entrypoint_fn, method_chain_args};
|
||||
use pulldown_cmark::Event::{
|
||||
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
|
||||
};
|
||||
use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
|
||||
use pulldown_cmark::Tag::{BlockQuote, CodeBlock, Heading, Item, Link, Paragraph};
|
||||
use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options};
|
||||
use rustc_ast::ast::Attribute;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{AnonConst, Expr};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
@ -538,7 +538,16 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
|
||||
|
||||
suspicious_doc_comments::check(cx, attrs);
|
||||
|
||||
let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true);
|
||||
let (fragments, _) = attrs_to_doc_fragments(
|
||||
attrs.iter().filter_map(|attr| {
|
||||
if in_external_macro(cx.sess(), attr.span) {
|
||||
None
|
||||
} else {
|
||||
Some((attr, None))
|
||||
}
|
||||
}),
|
||||
true,
|
||||
);
|
||||
let mut doc = fragments.iter().fold(String::new(), |mut acc, fragment| {
|
||||
add_doc_fragment(&mut acc, fragment);
|
||||
acc
|
||||
@ -602,6 +611,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||
let mut text_to_check: Vec<(CowStr<'_>, Range<usize>, isize)> = Vec::new();
|
||||
let mut paragraph_range = 0..0;
|
||||
let mut code_level = 0;
|
||||
let mut blockquote_level = 0;
|
||||
|
||||
for (event, range) in events {
|
||||
match event {
|
||||
@ -610,8 +620,14 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||
code_level += 1;
|
||||
} else if tag.starts_with("</code") {
|
||||
code_level -= 1;
|
||||
} else if tag.starts_with("<blockquote") || tag.starts_with("<q") {
|
||||
blockquote_level += 1;
|
||||
} else if tag.starts_with("</blockquote") || tag.starts_with("</q") {
|
||||
blockquote_level -= 1;
|
||||
}
|
||||
},
|
||||
Start(BlockQuote) => blockquote_level += 1,
|
||||
End(BlockQuote) => blockquote_level -= 1,
|
||||
Start(CodeBlock(ref kind)) => {
|
||||
in_code = true;
|
||||
if let CodeBlockKind::Fenced(lang) = kind {
|
||||
@ -663,7 +679,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||
} else {
|
||||
for (text, range, assoc_code_level) in text_to_check {
|
||||
if let Some(span) = fragments.span(cx, range) {
|
||||
markdown::check(cx, valid_idents, &text, span, assoc_code_level);
|
||||
markdown::check(cx, valid_idents, &text, span, assoc_code_level, blockquote_level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,24 +49,22 @@ declare_clippy_lint! {
|
||||
declare_lint_pass!(ElseIfWithoutElse => [ELSE_IF_WITHOUT_ELSE]);
|
||||
|
||||
impl EarlyLintPass for ElseIfWithoutElse {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, mut item: &Expr) {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
|
||||
if in_external_macro(cx.sess(), item.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
while let ExprKind::If(_, _, Some(ref els)) = item.kind {
|
||||
if let ExprKind::If(_, _, None) = els.kind {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
ELSE_IF_WITHOUT_ELSE,
|
||||
els.span,
|
||||
"`if` expression with an `else if`, but without a final `else`",
|
||||
None,
|
||||
"add an `else` block here",
|
||||
);
|
||||
}
|
||||
|
||||
item = els;
|
||||
if let ExprKind::If(_, _, Some(ref els)) = item.kind
|
||||
&& let ExprKind::If(_, _, None) = els.kind
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
ELSE_IF_WITHOUT_ELSE,
|
||||
els.span,
|
||||
"`if` expression with an `else if`, but without a final `else`",
|
||||
None,
|
||||
"add an `else` block here",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -358,7 +358,7 @@ struct InsertSearcher<'cx, 'tcx> {
|
||||
can_use_entry: bool,
|
||||
/// Whether this expression is the final expression in this code path. This may be a statement.
|
||||
in_tail_pos: bool,
|
||||
// Is this expression a single insert. A slightly better suggestion can be made in this case.
|
||||
/// Is this expression a single insert. A slightly better suggestion can be made in this case.
|
||||
is_single_insert: bool,
|
||||
/// If the visitor has seen the map being used.
|
||||
is_map_used: bool,
|
||||
@ -431,6 +431,9 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
|
||||
self.is_single_insert = false;
|
||||
self.visit_expr(e);
|
||||
}
|
||||
if let Some(els) = &l.els {
|
||||
self.visit_block(els);
|
||||
}
|
||||
},
|
||||
StmtKind::Item(_) => {
|
||||
self.allow_insert_closure &= !self.in_tail_pos;
|
||||
|
@ -250,7 +250,7 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A `Result` is at least as large as the `Err`-variant. While we
|
||||
/// expect that variant to be seldomly used, the compiler needs to reserve
|
||||
/// expect that variant to be seldom used, the compiler needs to reserve
|
||||
/// and move that much memory every single time.
|
||||
/// Furthermore, errors are often simply passed up the call-stack, making
|
||||
/// use of the `?`-operator and its type-conversion mechanics. If the
|
||||
|
50
clippy_lints/src/integer_division_remainder_used.rs
Normal file
50
clippy_lints/src/integer_division_remainder_used.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_ast::BinOpKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of division (/) and remainder (%) operations
|
||||
/// when performed on any integer types using the default Div and Rem trait implementations.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// In cryptographic contexts, division can result in timing sidechannel vulnerabilities,
|
||||
/// and needs to be replaced with constant-time code instead (e.g. Barrett reduction).
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let my_div = 10 / 2;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let my_div = 10 >> 1;
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub INTEGER_DIVISION_REMAINDER_USED,
|
||||
restriction,
|
||||
"use of disallowed default division and remainder operations"
|
||||
}
|
||||
|
||||
declare_lint_pass!(IntegerDivisionRemainderUsed => [INTEGER_DIVISION_REMAINDER_USED]);
|
||||
|
||||
impl LateLintPass<'_> for IntegerDivisionRemainderUsed {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Binary(op, lhs, rhs) = &expr.kind
|
||||
&& let BinOpKind::Div | BinOpKind::Rem = op.node
|
||||
&& let lhs_ty = cx.typeck_results().expr_ty(lhs)
|
||||
&& let rhs_ty = cx.typeck_results().expr_ty(rhs)
|
||||
&& let ty::Int(_) | ty::Uint(_) = lhs_ty.peel_refs().kind()
|
||||
&& let ty::Int(_) | ty::Uint(_) = rhs_ty.peel_refs().kind()
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
INTEGER_DIVISION_REMAINDER_USED,
|
||||
expr.span.source_callsite(),
|
||||
&format!("use of {} has been disallowed in this context", op.node.as_str()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::path_to_local_id;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
@ -122,9 +122,10 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
||||
value=snippet(cx, value.span, "<value>"),
|
||||
default=snippet(cx, default.span, "<default>"),
|
||||
);
|
||||
span_lint_and_then(
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
USELESS_LET_IF_SEQ,
|
||||
local.hir_id,
|
||||
span,
|
||||
"`if _ { .. } else { .. }` is an expression",
|
||||
|diag| {
|
||||
|
@ -172,6 +172,7 @@ mod init_numbered_fields;
|
||||
mod inline_fn_without_body;
|
||||
mod instant_subtraction;
|
||||
mod int_plus_one;
|
||||
mod integer_division_remainder_used;
|
||||
mod invalid_upcast_comparisons;
|
||||
mod item_name_repetitions;
|
||||
mod items_after_statements;
|
||||
@ -211,6 +212,7 @@ mod manual_retain;
|
||||
mod manual_slice_size_calculation;
|
||||
mod manual_string_new;
|
||||
mod manual_strip;
|
||||
mod manual_unwrap_or_default;
|
||||
mod map_unit_fn;
|
||||
mod match_result_ok;
|
||||
mod matches;
|
||||
@ -373,6 +375,7 @@ mod visibility;
|
||||
mod wildcard_imports;
|
||||
mod write;
|
||||
mod zero_div_zero;
|
||||
mod zero_repeat_side_effects;
|
||||
mod zero_sized_map_values;
|
||||
// end lints modules, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
@ -1119,7 +1122,10 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||
store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv())));
|
||||
store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl));
|
||||
store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations));
|
||||
store.register_late_pass(|_| Box::new(assigning_clones::AssigningClones));
|
||||
store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(msrv())));
|
||||
store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects));
|
||||
store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault));
|
||||
store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
@ -273,7 +273,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
|
||||
}
|
||||
return false; // no need to walk further *on the variable*
|
||||
},
|
||||
Res::Def(DefKind::Static{..} | DefKind::Const, ..) => {
|
||||
Res::Def(DefKind::Static { .. } | DefKind::Const, ..) => {
|
||||
if index_used_directly {
|
||||
self.indexed_directly.insert(
|
||||
seqvar.segments[0].ident.name,
|
||||
|
@ -255,9 +255,7 @@ fn never_loop_expr<'tcx>(
|
||||
InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => {
|
||||
NeverLoopResult::Normal
|
||||
},
|
||||
InlineAsmOperand::Label { block } => {
|
||||
never_loop_block(cx, block, local_labels, main_loop_id)
|
||||
}
|
||||
InlineAsmOperand::Label { block } => never_loop_block(cx, block, local_labels, main_loop_id),
|
||||
})),
|
||||
ExprKind::OffsetOf(_, _)
|
||||
| ExprKind::Yield(_, _)
|
||||
|
@ -1,62 +1,41 @@
|
||||
use super::UNUSED_ENUMERATE_INDEX;
|
||||
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{pat_is_wild, sugg};
|
||||
use clippy_utils::{match_def_path, pat_is_wild, sugg};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
|
||||
/// Checks for the `UNUSED_ENUMERATE_INDEX` lint.
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
|
||||
let PatKind::Tuple([index, elem], _) = pat.kind else {
|
||||
return;
|
||||
};
|
||||
|
||||
let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind else {
|
||||
return;
|
||||
};
|
||||
|
||||
let ty = cx.typeck_results().expr_ty(arg);
|
||||
|
||||
if !pat_is_wild(cx, &index.kind, body) {
|
||||
return;
|
||||
///
|
||||
/// The lint is also partially implemented in `clippy_lints/src/methods/unused_enumerate_index.rs`.
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_>, body: &'tcx Expr<'tcx>) {
|
||||
if let PatKind::Tuple([index, elem], _) = pat.kind
|
||||
&& let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind
|
||||
&& let ty = cx.typeck_results().expr_ty(arg)
|
||||
&& pat_is_wild(cx, &index.kind, body)
|
||||
&& let ty::Adt(base, _) = *ty.kind()
|
||||
&& match_def_path(cx, base.did(), &clippy_utils::paths::CORE_ITER_ENUMERATE_STRUCT)
|
||||
&& let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id)
|
||||
&& match_def_path(cx, call_id, &clippy_utils::paths::CORE_ITER_ENUMERATE_METHOD)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNUSED_ENUMERATE_INDEX,
|
||||
arg.span,
|
||||
"you seem to use `.enumerate()` and immediately discard the index",
|
||||
|diag| {
|
||||
let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter");
|
||||
multispan_sugg(
|
||||
diag,
|
||||
"remove the `.enumerate()` call",
|
||||
vec![
|
||||
(pat.span, snippet(cx, elem.span, "..").into_owned()),
|
||||
(arg.span, base_iter.to_string()),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let name = match *ty.kind() {
|
||||
ty::Adt(base, _substs) => cx.tcx.def_path_str(base.did()),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if name != "std::iter::Enumerate" && name != "core::iter::Enumerate" {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let call_name = cx.tcx.def_path_str(call_id);
|
||||
|
||||
if call_name != "std::iter::Iterator::enumerate" && call_name != "core::iter::Iterator::enumerate" {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNUSED_ENUMERATE_INDEX,
|
||||
arg.span,
|
||||
"you seem to use `.enumerate()` and immediately discard the index",
|
||||
|diag| {
|
||||
let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter");
|
||||
multispan_sugg(
|
||||
diag,
|
||||
"remove the `.enumerate()` call",
|
||||
vec![
|
||||
(pat.span, snippet(cx, elem.span, "..").into_owned()),
|
||||
(arg.span, base_iter.to_string()),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
|
||||
Res::Local(hir_id) => {
|
||||
self.ids.insert(hir_id);
|
||||
},
|
||||
Res::Def(DefKind::Static{..}, def_id) => {
|
||||
Res::Def(DefKind::Static { .. }, def_id) => {
|
||||
let mutable = self.cx.tcx.is_mutable_static(def_id);
|
||||
self.def_ids.insert(def_id, mutable);
|
||||
},
|
||||
|
@ -2,7 +2,7 @@ use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
||||
use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq};
|
||||
use clippy_utils::{match_def_path, paths, SpanlessEq};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
@ -69,17 +69,16 @@ impl_lint_pass!(ManualRetain => [MANUAL_RETAIN]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualRetain {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if let Some(parent_expr) = get_parent_expr(cx, expr)
|
||||
&& let Assign(left_expr, collect_expr, _) = &parent_expr.kind
|
||||
if let Assign(left_expr, collect_expr, _) = &expr.kind
|
||||
&& let hir::ExprKind::MethodCall(seg, ..) = &collect_expr.kind
|
||||
&& seg.args.is_none()
|
||||
&& let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind
|
||||
&& let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id)
|
||||
&& cx.tcx.is_diagnostic_item(sym::iterator_collect_fn, collect_def_id)
|
||||
{
|
||||
check_into_iter(cx, left_expr, target_expr, parent_expr.span, &self.msrv);
|
||||
check_iter(cx, left_expr, target_expr, parent_expr.span, &self.msrv);
|
||||
check_to_owned(cx, left_expr, target_expr, parent_expr.span, &self.msrv);
|
||||
check_into_iter(cx, left_expr, target_expr, expr.span, &self.msrv);
|
||||
check_iter(cx, left_expr, target_expr, expr.span, &self.msrv);
|
||||
check_to_owned(cx, left_expr, target_expr, expr.span, &self.msrv);
|
||||
}
|
||||
}
|
||||
|
||||
|
181
clippy_lints/src/manual_unwrap_or_default.rs
Normal file
181
clippy_lints/src/manual_unwrap_or_default.rs
Normal file
@ -0,0 +1,181 @@
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_default_equivalent;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if a `match` or `if let` expression can be simplified using
|
||||
/// `.unwrap_or_default()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It can be done in one call with `.unwrap_or_default()`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x: Option<String> = Some(String::new());
|
||||
/// let y: String = match x {
|
||||
/// Some(v) => v,
|
||||
/// None => String::new(),
|
||||
/// };
|
||||
///
|
||||
/// let x: Option<Vec<String>> = Some(Vec::new());
|
||||
/// let y: Vec<String> = if let Some(v) = x {
|
||||
/// v
|
||||
/// } else {
|
||||
/// Vec::new()
|
||||
/// };
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let x: Option<String> = Some(String::new());
|
||||
/// let y: String = x.unwrap_or_default();
|
||||
///
|
||||
/// let x: Option<Vec<String>> = Some(Vec::new());
|
||||
/// let y: Vec<String> = x.unwrap_or_default();
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub MANUAL_UNWRAP_OR_DEFAULT,
|
||||
suspicious,
|
||||
"check if a `match` or `if let` can be simplified with `unwrap_or_default`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ManualUnwrapOrDefault => [MANUAL_UNWRAP_OR_DEFAULT]);
|
||||
|
||||
fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<HirId> {
|
||||
if let PatKind::TupleStruct(QPath::Resolved(_, path), &[pat], _) = pat.kind
|
||||
&& let Some(def_id) = path.res.opt_def_id()
|
||||
// Since it comes from a pattern binding, we need to get the parent to actually match
|
||||
// against it.
|
||||
&& let Some(def_id) = cx.tcx.opt_parent(def_id)
|
||||
&& cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id)
|
||||
{
|
||||
let mut bindings = Vec::new();
|
||||
pat.each_binding(|_, id, _, _| bindings.push(id));
|
||||
if let &[id] = bindings.as_slice() {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if let PatKind::Path(QPath::Resolved(_, path)) = arm.pat.kind
|
||||
&& let Some(def_id) = path.res.opt_def_id()
|
||||
// Since it comes from a pattern binding, we need to get the parent to actually match
|
||||
// against it.
|
||||
&& let Some(def_id) = cx.tcx.opt_parent(def_id)
|
||||
&& cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id)
|
||||
{
|
||||
Some(arm.body)
|
||||
} else if let PatKind::Wild = arm.pat.kind {
|
||||
// We consider that the `Some` check will filter it out if it's not right.
|
||||
Some(arm.body)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_some_and_none_bodies<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
arm1: &'tcx Arm<'tcx>,
|
||||
arm2: &'tcx Arm<'tcx>,
|
||||
) -> Option<((&'tcx Expr<'tcx>, HirId), &'tcx Expr<'tcx>)> {
|
||||
if let Some(binding_id) = get_some(cx, arm1.pat)
|
||||
&& let Some(body_none) = get_none(cx, arm2)
|
||||
{
|
||||
Some(((arm1.body, binding_id), body_none))
|
||||
} else if let Some(binding_id) = get_some(cx, arm2.pat)
|
||||
&& let Some(body_none) = get_none(cx, arm1)
|
||||
{
|
||||
Some(((arm2.body, binding_id), body_none))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
let ExprKind::Match(match_expr, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar) = expr.kind else {
|
||||
return false;
|
||||
};
|
||||
// We don't want conditions on the arms to simplify things.
|
||||
if arm1.guard.is_none()
|
||||
&& arm2.guard.is_none()
|
||||
// We check that the returned type implements the `Default` trait.
|
||||
&& let match_ty = cx.typeck_results().expr_ty(expr)
|
||||
&& let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
|
||||
&& implements_trait(cx, match_ty, default_trait_id, &[])
|
||||
// We now get the bodies for both the `Some` and `None` arms.
|
||||
&& let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2)
|
||||
// We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`.
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = body_some.peel_blocks().kind
|
||||
&& let Res::Local(local_id) = path.res
|
||||
&& local_id == binding_id
|
||||
// We now check the `None` arm is calling a method equivalent to `Default::default`.
|
||||
&& let body_none = body_none.peel_blocks()
|
||||
&& is_default_equivalent(cx, body_none)
|
||||
&& let Some(match_expr_snippet) = snippet_opt(cx, match_expr.span)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_UNWRAP_OR_DEFAULT,
|
||||
expr.span,
|
||||
"match can be simplified with `.unwrap_or_default()`",
|
||||
"replace it with",
|
||||
format!("{match_expr_snippet}.unwrap_or_default()"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn handle_if_let<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let ExprKind::If(cond, if_block, Some(else_expr)) = expr.kind
|
||||
&& let ExprKind::Let(let_) = cond.kind
|
||||
&& let ExprKind::Block(_, _) = else_expr.kind
|
||||
// We check that the returned type implements the `Default` trait.
|
||||
&& let match_ty = cx.typeck_results().expr_ty(expr)
|
||||
&& let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
|
||||
&& implements_trait(cx, match_ty, default_trait_id, &[])
|
||||
&& let Some(binding_id) = get_some(cx, let_.pat)
|
||||
// We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`.
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = if_block.peel_blocks().kind
|
||||
&& let Res::Local(local_id) = path.res
|
||||
&& local_id == binding_id
|
||||
// We now check the `None` arm is calling a method equivalent to `Default::default`.
|
||||
&& let body_else = else_expr.peel_blocks()
|
||||
&& is_default_equivalent(cx, body_else)
|
||||
&& let Some(if_let_expr_snippet) = snippet_opt(cx, let_.init.span)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_UNWRAP_OR_DEFAULT,
|
||||
expr.span,
|
||||
"if let can be simplified with `.unwrap_or_default()`",
|
||||
"replace it with",
|
||||
format!("{if_let_expr_snippet}.unwrap_or_default()"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
if !handle_match(cx, expr) {
|
||||
handle_if_let(cx, expr);
|
||||
}
|
||||
}
|
||||
}
|
@ -55,23 +55,15 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
|
||||
};
|
||||
|
||||
let ty = cx.typeck_results().expr_ty(ex);
|
||||
if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
|
||||
check_single_pattern(cx, ex, arms, expr, els);
|
||||
check_opt_like(cx, ex, arms, expr, ty, els);
|
||||
if (*ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id)) &&
|
||||
(check_single_pattern(arms) || check_opt_like(cx, arms, ty)) {
|
||||
report_single_pattern(cx, ex, arms, expr, els);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_single_pattern(
|
||||
cx: &LateContext<'_>,
|
||||
ex: &Expr<'_>,
|
||||
arms: &[Arm<'_>],
|
||||
expr: &Expr<'_>,
|
||||
els: Option<&Expr<'_>>,
|
||||
) {
|
||||
if is_wild(arms[1].pat) {
|
||||
report_single_pattern(cx, ex, arms, expr, els);
|
||||
}
|
||||
fn check_single_pattern(arms: &[Arm<'_>]) -> bool {
|
||||
is_wild(arms[1].pat)
|
||||
}
|
||||
|
||||
fn report_single_pattern(
|
||||
@ -140,19 +132,10 @@ fn report_single_pattern(
|
||||
span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app);
|
||||
}
|
||||
|
||||
fn check_opt_like<'a>(
|
||||
cx: &LateContext<'a>,
|
||||
ex: &Expr<'_>,
|
||||
arms: &[Arm<'_>],
|
||||
expr: &Expr<'_>,
|
||||
ty: Ty<'a>,
|
||||
els: Option<&Expr<'_>>,
|
||||
) {
|
||||
fn check_opt_like<'a>(cx: &LateContext<'a>, arms: &[Arm<'_>], ty: Ty<'a>) -> bool {
|
||||
// We don't want to lint if the second arm contains an enum which could
|
||||
// have more variants in the future.
|
||||
if form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) {
|
||||
report_single_pattern(cx, ex, arms, expr, els);
|
||||
}
|
||||
form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat)
|
||||
}
|
||||
|
||||
/// Returns `true` if all of the types in the pattern are enums which we know
|
||||
|
@ -91,7 +91,7 @@ pub(super) fn check<'tcx>(
|
||||
},
|
||||
hir::ExprKind::Path(ref p) => matches!(
|
||||
cx.qpath_res(p, arg.hir_id),
|
||||
hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static{..}, _)
|
||||
hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static { .. }, _)
|
||||
),
|
||||
_ => false,
|
||||
}
|
||||
|
49
clippy_lints/src/methods/is_empty.rs
Normal file
49
clippy_lints/src/methods/is_empty.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use clippy_utils::consts::constant_is_empty;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{find_binding_init, path_to_local};
|
||||
use rustc_hir::{Expr, HirId};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::CONST_IS_EMPTY;
|
||||
|
||||
/// Expression whose initialization depend on a constant conditioned by a `#[cfg(…)]` directive will
|
||||
/// not trigger the lint.
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_>) {
|
||||
if in_external_macro(cx.sess(), expr.span) || !receiver.span.eq_ctxt(expr.span) {
|
||||
return;
|
||||
}
|
||||
let init_expr = expr_or_init(cx, receiver);
|
||||
if !receiver.span.eq_ctxt(init_expr.span) {
|
||||
return;
|
||||
}
|
||||
if let Some(init_is_empty) = constant_is_empty(cx, init_expr) {
|
||||
span_lint(
|
||||
cx,
|
||||
CONST_IS_EMPTY,
|
||||
expr.span,
|
||||
&format!("this expression always evaluates to {init_is_empty:?}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool {
|
||||
cx.tcx
|
||||
.hir()
|
||||
.parent_id_iter(id)
|
||||
.any(|id| cx.tcx.hir().attrs(id).iter().any(|attr| attr.has_name(sym::cfg)))
|
||||
}
|
||||
|
||||
/// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization
|
||||
/// value depends on a `#[cfg(…)]` directive.
|
||||
fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
|
||||
while let Some(init) = path_to_local(expr)
|
||||
.and_then(|id| find_binding_init(cx, id))
|
||||
.filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
|
||||
.filter(|init| !is_under_cfg(cx, init.hir_id))
|
||||
{
|
||||
expr = init;
|
||||
}
|
||||
expr
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
use super::utils::derefs_to_slice;
|
||||
use crate::methods::iter_nth_zero;
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::get_type_diagnostic_name;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
|
||||
use super::ITER_NTH;
|
||||
|
||||
@ -12,28 +12,33 @@ pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &hir::Expr<'_>,
|
||||
iter_recv: &'tcx hir::Expr<'tcx>,
|
||||
nth_recv: &hir::Expr<'_>,
|
||||
nth_arg: &hir::Expr<'_>,
|
||||
is_mut: bool,
|
||||
) {
|
||||
let mut_str = if is_mut { "_mut" } else { "" };
|
||||
let caller_type = if derefs_to_slice(cx, iter_recv, cx.typeck_results().expr_ty(iter_recv)).is_some() {
|
||||
"slice"
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::Vec) {
|
||||
"`Vec`"
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::VecDeque) {
|
||||
"`VecDeque`"
|
||||
} else {
|
||||
iter_nth_zero::check(cx, expr, nth_recv, nth_arg);
|
||||
return; // caller is not a type that we want to lint
|
||||
iter_method: &str,
|
||||
iter_span: Span,
|
||||
nth_span: Span,
|
||||
) -> bool {
|
||||
let caller_type = match get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(iter_recv).peel_refs()) {
|
||||
Some(sym::Vec) => "`Vec`",
|
||||
Some(sym::VecDeque) => "`VecDeque`",
|
||||
_ if cx.typeck_results().expr_ty_adjusted(iter_recv).peel_refs().is_slice() => "slice",
|
||||
// caller is not a type that we want to lint
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
span_lint_and_help(
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
ITER_NTH,
|
||||
expr.span,
|
||||
&format!("called `.iter{mut_str}().nth()` on a {caller_type}"),
|
||||
None,
|
||||
&format!("calling `.get{mut_str}()` is both faster and more readable"),
|
||||
&format!("called `.{iter_method}().nth()` on a {caller_type}"),
|
||||
|diag| {
|
||||
let get_method = if iter_method == "iter_mut" { "get_mut" } else { "get" };
|
||||
diag.span_suggestion_verbose(
|
||||
iter_span.to(nth_span),
|
||||
format!("`{get_method}` is equivalent but more concise"),
|
||||
get_method,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
|
@ -86,8 +86,11 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Call(call, [_]) => {
|
||||
if let hir::ExprKind::Path(qpath) = call.kind {
|
||||
hir::ExprKind::Call(call, args) => {
|
||||
if let hir::ExprKind::Path(qpath) = call.kind
|
||||
&& let [arg] = args
|
||||
&& ident_eq(name, arg)
|
||||
{
|
||||
handle_path(cx, call, &qpath, e, recv);
|
||||
}
|
||||
},
|
||||
@ -118,7 +121,9 @@ fn handle_path(
|
||||
if let ty::Adt(_, args) = cx.typeck_results().expr_ty(recv).kind()
|
||||
&& let args = args.as_slice()
|
||||
&& let Some(ty) = args.iter().find_map(|generic_arg| generic_arg.as_type())
|
||||
&& ty.is_ref()
|
||||
&& let ty::Ref(_, ty, Mutability::Not) = ty.kind()
|
||||
&& let ty::FnDef(_, lst) = cx.typeck_results().expr_ty(arg).kind()
|
||||
&& lst.iter().all(|l| l.as_type() == Some(*ty))
|
||||
{
|
||||
lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs()));
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ mod inefficient_to_string;
|
||||
mod inspect_for_each;
|
||||
mod into_iter_on_ref;
|
||||
mod is_digit_ascii_radix;
|
||||
mod is_empty;
|
||||
mod iter_cloned_collect;
|
||||
mod iter_count;
|
||||
mod iter_filter;
|
||||
@ -118,6 +119,7 @@ mod unnecessary_literal_unwrap;
|
||||
mod unnecessary_result_map_or_else;
|
||||
mod unnecessary_sort_by;
|
||||
mod unnecessary_to_owned;
|
||||
mod unused_enumerate_index;
|
||||
mod unwrap_expect_used;
|
||||
mod useless_asref;
|
||||
mod utils;
|
||||
@ -1235,12 +1237,11 @@ declare_clippy_lint! {
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `.iter().nth()` (and the related
|
||||
/// `.iter_mut().nth()`) on standard library types with *O*(1) element access.
|
||||
/// Checks for usage of `.iter().nth()`/`.iter_mut().nth()` on standard library types that have
|
||||
/// equivalent `.get()`/`.get_mut()` methods.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `.get()` and `.get_mut()` are more efficient and more
|
||||
/// readable.
|
||||
/// `.get()` and `.get_mut()` are equivalent but more concise.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
@ -1256,7 +1257,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub ITER_NTH,
|
||||
perf,
|
||||
style,
|
||||
"using `.iter().nth()` on a standard library type with O(1) element access"
|
||||
}
|
||||
|
||||
@ -2848,7 +2849,7 @@ declare_clippy_lint! {
|
||||
/// the file is created from scratch, or ensure `truncate` is
|
||||
/// called so that the truncation behaviour is explicit. `truncate(true)`
|
||||
/// will ensure the file is entirely overwritten with new data, whereas
|
||||
/// `truncate(false)` will explicitely keep the default behavior.
|
||||
/// `truncate(false)` will explicitly keep the default behavior.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,no_run
|
||||
@ -2862,7 +2863,7 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// OpenOptions::new().create(true).truncate(true);
|
||||
/// ```
|
||||
#[clippy::version = "1.75.0"]
|
||||
#[clippy::version = "1.77.0"]
|
||||
pub SUSPICIOUS_OPEN_OPTIONS,
|
||||
suspicious,
|
||||
"suspicious combination of options for opening a file"
|
||||
@ -3182,8 +3183,8 @@ declare_clippy_lint! {
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Checks an argument of `seek` method of `Seek` trait
|
||||
/// and if it start seek from `SeekFrom::Current(0)`, suggests `stream_position` instead.
|
||||
/// Checks if the `seek` method of the `Seek` trait is called with `SeekFrom::Current(0)`,
|
||||
/// and if it is, suggests using `stream_position` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
@ -3590,7 +3591,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
#[clippy::version = "1.73.0"]
|
||||
pub READONLY_WRITE_LOCK,
|
||||
nursery,
|
||||
perf,
|
||||
"acquiring a write lock when a read lock would work"
|
||||
}
|
||||
|
||||
@ -3816,7 +3817,7 @@ declare_clippy_lint! {
|
||||
/// ```no_run
|
||||
/// let _ = std::iter::empty::<Result<i32, ()>>().flatten();
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
#[clippy::version = "1.77.0"]
|
||||
pub RESULT_FILTER_MAP,
|
||||
complexity,
|
||||
"filtering `Result` for `Ok` then force-unwrapping, which can be one type-safe operation"
|
||||
@ -3825,7 +3826,7 @@ declare_clippy_lint! {
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `.filter(Option::is_some)` that may be replaced with a `.flatten()` call.
|
||||
/// This lint will require additional changes to the follow-up calls as it appects the type.
|
||||
/// This lint will require additional changes to the follow-up calls as it affects the type.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This pattern is often followed by manual unwrapping of the `Option`. The simplification
|
||||
@ -3842,7 +3843,7 @@ declare_clippy_lint! {
|
||||
/// // example code which does not raise clippy warning
|
||||
/// vec![Some(1)].into_iter().flatten();
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
#[clippy::version = "1.77.0"]
|
||||
pub ITER_FILTER_IS_SOME,
|
||||
pedantic,
|
||||
"filtering an iterator over `Option`s for `Some` can be achieved with `flatten`"
|
||||
@ -3851,7 +3852,7 @@ declare_clippy_lint! {
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `.filter(Result::is_ok)` that may be replaced with a `.flatten()` call.
|
||||
/// This lint will require additional changes to the follow-up calls as it appects the type.
|
||||
/// This lint will require additional changes to the follow-up calls as it affects the type.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This pattern is often followed by manual unwrapping of `Result`. The simplification
|
||||
@ -3868,7 +3869,7 @@ declare_clippy_lint! {
|
||||
/// // example code which does not raise clippy warning
|
||||
/// vec![Ok::<i32, String>(1)].into_iter().flatten();
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
#[clippy::version = "1.77.0"]
|
||||
pub ITER_FILTER_IS_OK,
|
||||
pedantic,
|
||||
"filtering an iterator over `Result`s for `Ok` can be achieved with `flatten`"
|
||||
@ -3895,7 +3896,7 @@ declare_clippy_lint! {
|
||||
/// option.is_some_and(|a| a > 10);
|
||||
/// result.is_ok_and(|a| a > 10);
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
#[clippy::version = "1.77.0"]
|
||||
pub MANUAL_IS_VARIANT_AND,
|
||||
pedantic,
|
||||
"using `.map(f).unwrap_or_default()`, which is more succinctly expressed as `is_some_and(f)` or `is_ok_and(f)`"
|
||||
@ -3925,7 +3926,7 @@ declare_clippy_lint! {
|
||||
/// `"\r\n"`), for example during the parsing of a specific file format in which precisely one newline type is
|
||||
/// valid.
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
#[clippy::version = "1.77.0"]
|
||||
pub STR_SPLIT_AT_NEWLINE,
|
||||
pedantic,
|
||||
"splitting a trimmed string at hard-coded newlines"
|
||||
@ -4044,6 +4045,31 @@ declare_clippy_lint! {
|
||||
"calling `.get().is_some()` or `.get().is_none()` instead of `.contains()` or `.contains_key()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// It identifies calls to `.is_empty()` on constant values.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// String literals and constant values are known at compile time. Checking if they
|
||||
/// are empty will always return the same value. This might not be the intention of
|
||||
/// the expression.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let value = "";
|
||||
/// if value.is_empty() {
|
||||
/// println!("the string is empty");
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// println!("the string is empty");
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub CONST_IS_EMPTY,
|
||||
suspicious,
|
||||
"is_empty() called on strings known at compile time"
|
||||
}
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Msrv,
|
||||
@ -4092,6 +4118,7 @@ impl_lint_pass!(Methods => [
|
||||
CLONE_ON_COPY,
|
||||
CLONE_ON_REF_PTR,
|
||||
COLLAPSIBLE_STR_REPLACE,
|
||||
CONST_IS_EMPTY,
|
||||
ITER_OVEREAGER_CLONED,
|
||||
CLONED_INSTEAD_OF_COPIED,
|
||||
FLAT_MAP_OPTION,
|
||||
@ -4403,6 +4430,7 @@ impl Methods {
|
||||
zst_offset::check(cx, expr, recv);
|
||||
},
|
||||
("all", [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
|
||||
iter_overeager_cloned::check(
|
||||
cx,
|
||||
@ -4421,23 +4449,26 @@ impl Methods {
|
||||
unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
|
||||
}
|
||||
},
|
||||
("any", [arg]) => match method_call(recv) {
|
||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
recv2,
|
||||
iter_overeager_cloned::Op::NeedlessMove(arg),
|
||||
false,
|
||||
),
|
||||
Some(("chars", recv, _, _, _))
|
||||
if let ExprKind::Closure(arg) = arg.kind
|
||||
&& let body = cx.tcx.hir().body(arg.body)
|
||||
&& let [param] = body.params =>
|
||||
{
|
||||
string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
|
||||
},
|
||||
_ => {},
|
||||
("any", [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
match method_call(recv) {
|
||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
recv2,
|
||||
iter_overeager_cloned::Op::NeedlessMove(arg),
|
||||
false,
|
||||
),
|
||||
Some(("chars", recv, _, _, _))
|
||||
if let ExprKind::Closure(arg) = arg.kind
|
||||
&& let body = cx.tcx.hir().body(arg.body)
|
||||
&& let [param] = body.params =>
|
||||
{
|
||||
string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
("arg", [arg]) => {
|
||||
suspicious_command_arg_space::check(cx, recv, arg, span);
|
||||
@ -4445,7 +4476,7 @@ impl Methods {
|
||||
("as_deref" | "as_deref_mut", []) => {
|
||||
needless_option_as_deref::check(cx, expr, recv, name);
|
||||
},
|
||||
("as_bytes" | "is_empty", []) => {
|
||||
("as_bytes", []) => {
|
||||
if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) {
|
||||
redundant_as_str::check(cx, expr, recv, as_str_span, span);
|
||||
}
|
||||
@ -4570,14 +4601,17 @@ impl Methods {
|
||||
}
|
||||
},
|
||||
("filter_map", [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
unnecessary_filter_map::check(cx, expr, arg, name);
|
||||
filter_map_bool_then::check(cx, expr, arg, call_span);
|
||||
filter_map_identity::check(cx, expr, arg, span);
|
||||
},
|
||||
("find_map", [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
unnecessary_filter_map::check(cx, expr, arg, name);
|
||||
},
|
||||
("flat_map", [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
flat_map_identity::check(cx, expr, arg, span);
|
||||
flat_map_option::check(cx, expr, arg, span);
|
||||
},
|
||||
@ -4599,17 +4633,20 @@ impl Methods {
|
||||
manual_try_fold::check(cx, expr, init, acc, call_span, &self.msrv);
|
||||
unnecessary_fold::check(cx, expr, init, acc, span);
|
||||
},
|
||||
("for_each", [arg]) => match method_call(recv) {
|
||||
Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
|
||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
recv2,
|
||||
iter_overeager_cloned::Op::NeedlessMove(arg),
|
||||
false,
|
||||
),
|
||||
_ => {},
|
||||
("for_each", [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
match method_call(recv) {
|
||||
Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
|
||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
recv2,
|
||||
iter_overeager_cloned::Op::NeedlessMove(arg),
|
||||
false,
|
||||
),
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
("get", [arg]) => {
|
||||
get_first::check(cx, expr, recv, arg);
|
||||
@ -4619,6 +4656,12 @@ impl Methods {
|
||||
("hash", [arg]) => {
|
||||
unit_hash::check(cx, expr, recv, arg);
|
||||
},
|
||||
("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);
|
||||
}
|
||||
is_empty::check(cx, expr, recv);
|
||||
},
|
||||
("is_file", []) => filetype_is_file::check(cx, expr, recv),
|
||||
("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, &self.msrv),
|
||||
("is_none", []) => check_is_some_is_none(cx, expr, recv, call_span, false),
|
||||
@ -4650,6 +4693,7 @@ impl Methods {
|
||||
},
|
||||
(name @ ("map" | "map_err"), [m_arg]) => {
|
||||
if name == "map" {
|
||||
unused_enumerate_index::check(cx, expr, recv, m_arg);
|
||||
map_clone::check(cx, expr, recv, m_arg, &self.msrv);
|
||||
match method_call(recv) {
|
||||
Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => {
|
||||
@ -4723,8 +4767,11 @@ impl Methods {
|
||||
iter_overeager_cloned::Op::LaterCloned,
|
||||
false,
|
||||
),
|
||||
Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
|
||||
Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
|
||||
Some((iter_method @ ("iter" | "iter_mut"), iter_recv, [], iter_span, _)) => {
|
||||
if !iter_nth::check(cx, expr, iter_recv, iter_method, iter_span, span) {
|
||||
iter_nth_zero::check(cx, expr, recv, n_arg);
|
||||
}
|
||||
},
|
||||
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
|
||||
},
|
||||
("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
|
||||
|
@ -25,6 +25,7 @@ pub(super) fn check<'tcx>(
|
||||
&& param1 == param2.as_str()
|
||||
{
|
||||
span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself");
|
||||
return;
|
||||
}
|
||||
|
||||
if SpanlessEq::new(cx).eq_expr(arg1, arg2) {
|
||||
|
135
clippy_lints/src/methods/unused_enumerate_index.rs
Normal file
135
clippy_lints/src/methods/unused_enumerate_index.rs
Normal file
@ -0,0 +1,135 @@
|
||||
use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_hir_and_then};
|
||||
use clippy_utils::paths::{CORE_ITER_ENUMERATE_METHOD, CORE_ITER_ENUMERATE_STRUCT};
|
||||
use clippy_utils::source::{snippet, snippet_opt};
|
||||
use clippy_utils::{expr_or_init, is_trait_method, match_def_path, pat_is_wild};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::AdtDef;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use crate::loops::UNUSED_ENUMERATE_INDEX;
|
||||
|
||||
/// Check for the `UNUSED_ENUMERATE_INDEX` lint outside of loops.
|
||||
///
|
||||
/// The lint is declared in `clippy_lints/src/loops/mod.rs`. There, the following pattern is
|
||||
/// checked:
|
||||
/// ```ignore
|
||||
/// for (_, x) in some_iter.enumerate() {
|
||||
/// // Index is ignored
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This `check` function checks for chained method calls constructs where we can detect that the
|
||||
/// index is unused. Currently, this checks only for the following patterns:
|
||||
/// ```ignore
|
||||
/// some_iter.enumerate().map_function(|(_, x)| ..)
|
||||
/// let x = some_iter.enumerate();
|
||||
/// x.map_function(|(_, x)| ..)
|
||||
/// ```
|
||||
/// where `map_function` is one of `all`, `any`, `filter_map`, `find_map`, `flat_map`, `for_each` or
|
||||
/// `map`.
|
||||
///
|
||||
/// # Preconditions
|
||||
/// This function must be called not on the `enumerate` call expression itself, but on any of the
|
||||
/// map functions listed above. It will ensure that `recv` is a `std::iter::Enumerate` instance and
|
||||
/// that the method call is one of the `std::iter::Iterator` trait.
|
||||
///
|
||||
/// * `call_expr`: The map function call expression
|
||||
/// * `recv`: The receiver of the call
|
||||
/// * `closure_arg`: The argument to the map function call containing the closure/function to apply
|
||||
pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) {
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv);
|
||||
if let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did)
|
||||
// If we call a method on a `std::iter::Enumerate` instance
|
||||
&& match_def_path(cx, recv_ty_defid, &CORE_ITER_ENUMERATE_STRUCT)
|
||||
// If we are calling a method of the `Iterator` trait
|
||||
&& is_trait_method(cx, call_expr, sym::Iterator)
|
||||
// And the map argument is a closure
|
||||
&& let ExprKind::Closure(closure) = closure_arg.kind
|
||||
&& let closure_body = cx.tcx.hir().body(closure.body)
|
||||
// And that closure has one argument ...
|
||||
&& let [closure_param] = closure_body.params
|
||||
// .. which is a tuple of 2 elements
|
||||
&& let PatKind::Tuple([index, elem], ..) = closure_param.pat.kind
|
||||
// And that the first element (the index) is either `_` or unused in the body
|
||||
&& pat_is_wild(cx, &index.kind, closure_body)
|
||||
// Try to find the initializer for `recv`. This is needed in case `recv` is a local_binding. In the
|
||||
// first example below, `expr_or_init` would return `recv`.
|
||||
// ```
|
||||
// iter.enumerate().map(|(_, x)| x)
|
||||
// ^^^^^^^^^^^^^^^^ `recv`, a call to `std::iter::Iterator::enumerate`
|
||||
//
|
||||
// let binding = iter.enumerate();
|
||||
// ^^^^^^^^^^^^^^^^ `recv_init_expr`
|
||||
// binding.map(|(_, x)| x)
|
||||
// ^^^^^^^ `recv`, not a call to `std::iter::Iterator::enumerate`
|
||||
// ```
|
||||
&& let recv_init_expr = expr_or_init(cx, recv)
|
||||
// Make sure the initializer is a method call. It may be that the `Enumerate` comes from something
|
||||
// that we cannot control.
|
||||
// This would for instance happen with:
|
||||
// ```
|
||||
// external_lib::some_function_returning_enumerate().map(|(_, x)| x)
|
||||
// ```
|
||||
&& let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = recv_init_expr.kind
|
||||
&& let Some(enumerate_defid) = cx.typeck_results().type_dependent_def_id(recv_init_expr.hir_id)
|
||||
// Make sure the method call is `std::iter::Iterator::enumerate`.
|
||||
&& match_def_path(cx, enumerate_defid, &CORE_ITER_ENUMERATE_METHOD)
|
||||
{
|
||||
// Check if the tuple type was explicit. It may be the type system _needs_ the type of the element
|
||||
// that would be explicited in the closure.
|
||||
let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) {
|
||||
// We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`.
|
||||
// Fallback to `..` if we fail getting either snippet.
|
||||
Some(ty_span) => snippet_opt(cx, elem.span)
|
||||
.and_then(|binding_name| snippet_opt(cx, ty_span).map(|ty_name| format!("{binding_name}: {ty_name}")))
|
||||
.unwrap_or_else(|| "..".to_string()),
|
||||
// Otherwise, we have no explicit type. We can replace with the binding name of the element.
|
||||
None => snippet(cx, elem.span, "..").into_owned(),
|
||||
};
|
||||
|
||||
// Suggest removing the tuple from the closure and the preceding call to `enumerate`, whose span we
|
||||
// can get from the `MethodCall`.
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
UNUSED_ENUMERATE_INDEX,
|
||||
recv_init_expr.hir_id,
|
||||
enumerate_span,
|
||||
"you seem to use `.enumerate()` and immediately discard the index",
|
||||
|diag| {
|
||||
multispan_sugg_with_applicability(
|
||||
diag,
|
||||
"remove the `.enumerate()` call",
|
||||
Applicability::MachineApplicable,
|
||||
vec![
|
||||
(closure_param.span, new_closure_param),
|
||||
(
|
||||
enumerate_span.with_lo(enumerate_recv.span.source_callsite().hi()),
|
||||
String::new(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the span of the explicit type of the element.
|
||||
///
|
||||
/// # Returns
|
||||
/// If the tuple argument:
|
||||
/// * Has no explicit type, returns `None`
|
||||
/// * Has an explicit tuple type with an implicit element type (`(usize, _)`), returns `None`
|
||||
/// * Has an explicit tuple type with an explicit element type (`(_, i32)`), returns the span for
|
||||
/// the element type.
|
||||
fn find_elem_explicit_type_span(fn_decl: &FnDecl<'_>) -> Option<Span> {
|
||||
if let [tuple_ty] = fn_decl.inputs
|
||||
&& let TyKind::Tup([_idx_ty, elem_ty]) = tuple_ty.kind
|
||||
&& !matches!(elem_ty.kind, TyKind::Err(..) | TyKind::Infer)
|
||||
{
|
||||
Some(elem_ty.span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
use clippy_utils::attrs::is_doc_hidden;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
@ -32,6 +33,13 @@ declare_clippy_lint! {
|
||||
"detects missing documentation for private members"
|
||||
}
|
||||
|
||||
macro_rules! note_prev_span_then_ret {
|
||||
($prev_span:expr, $span:expr) => {{
|
||||
$prev_span = Some($span);
|
||||
return;
|
||||
}};
|
||||
}
|
||||
|
||||
pub struct MissingDoc {
|
||||
/// Whether to **only** check for missing documentation in items visible within the current
|
||||
/// crate. For example, `pub(crate)` items.
|
||||
@ -39,6 +47,8 @@ pub struct MissingDoc {
|
||||
/// Stack of whether #[doc(hidden)] is set
|
||||
/// at each level which has lint attributes.
|
||||
doc_hidden_stack: Vec<bool>,
|
||||
/// Used to keep tracking of the previous item, field or variants etc, to get the search span.
|
||||
prev_span: Option<Span>,
|
||||
}
|
||||
|
||||
impl Default for MissingDoc {
|
||||
@ -54,6 +64,7 @@ impl MissingDoc {
|
||||
Self {
|
||||
crate_items_only,
|
||||
doc_hidden_stack: vec![false],
|
||||
prev_span: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +119,8 @@ impl MissingDoc {
|
||||
|
||||
let has_doc = attrs
|
||||
.iter()
|
||||
.any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
|
||||
.any(|a| a.doc_str().is_some() || Self::has_include(a.meta()))
|
||||
|| matches!(self.search_span(sp), Some(span) if span_to_snippet_contains_docs(cx, span));
|
||||
|
||||
if !has_doc {
|
||||
span_lint(
|
||||
@ -119,6 +131,32 @@ impl MissingDoc {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a span to search for doc comments manually.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// fn foo() { ... }
|
||||
/// ^^^^^^^^^^^^^^^^ prev_span
|
||||
/// ↑
|
||||
/// | search_span |
|
||||
/// ↓
|
||||
/// fn bar() { ... }
|
||||
/// ^^^^^^^^^^^^^^^^ cur_span
|
||||
/// ```
|
||||
fn search_span(&self, cur_span: Span) -> Option<Span> {
|
||||
let prev_span = self.prev_span?;
|
||||
let start_pos = if prev_span.contains(cur_span) {
|
||||
// In case when the prev_span is an entire struct, or enum,
|
||||
// and the current span is a field, or variant, we need to search from
|
||||
// the starting pos of the previous span.
|
||||
prev_span.lo()
|
||||
} else {
|
||||
prev_span.hi()
|
||||
};
|
||||
let search_span = cur_span.with_lo(start_pos).with_hi(cur_span.lo());
|
||||
Some(search_span)
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]);
|
||||
@ -138,6 +176,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
self.check_missing_docs_attrs(cx, CRATE_DEF_ID, attrs, cx.tcx.def_span(CRATE_DEF_ID), "the", "crate");
|
||||
}
|
||||
|
||||
fn check_crate_post(&mut self, _: &LateContext<'tcx>) {
|
||||
self.prev_span = None;
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) {
|
||||
match it.kind {
|
||||
hir::ItemKind::Fn(..) => {
|
||||
@ -145,7 +187,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
if it.ident.name == sym::main {
|
||||
let at_root = cx.tcx.local_parent(it.owner_id.def_id) == CRATE_DEF_ID;
|
||||
if at_root {
|
||||
return;
|
||||
note_prev_span_then_ret!(self.prev_span, it.span);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -164,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
| hir::ItemKind::ForeignMod { .. }
|
||||
| hir::ItemKind::GlobalAsm(..)
|
||||
| hir::ItemKind::Impl { .. }
|
||||
| hir::ItemKind::Use(..) => return,
|
||||
| hir::ItemKind::Use(..) => note_prev_span_then_ret!(self.prev_span, it.span),
|
||||
};
|
||||
|
||||
let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id());
|
||||
@ -173,6 +215,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
if !is_from_proc_macro(cx, it) {
|
||||
self.check_missing_docs_attrs(cx, it.owner_id.def_id, attrs, it.span, article, desc);
|
||||
}
|
||||
self.prev_span = Some(it.span);
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) {
|
||||
@ -182,16 +225,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
if !is_from_proc_macro(cx, trait_item) {
|
||||
self.check_missing_docs_attrs(cx, trait_item.owner_id.def_id, attrs, trait_item.span, article, desc);
|
||||
}
|
||||
self.prev_span = Some(trait_item.span);
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
|
||||
// If the method is an impl for a trait, don't doc.
|
||||
if let Some(cid) = cx.tcx.associated_item(impl_item.owner_id).impl_container(cx.tcx) {
|
||||
if cx.tcx.impl_trait_ref(cid).is_some() {
|
||||
return;
|
||||
note_prev_span_then_ret!(self.prev_span, impl_item.span);
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
note_prev_span_then_ret!(self.prev_span, impl_item.span);
|
||||
}
|
||||
|
||||
let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id());
|
||||
@ -199,6 +243,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
if !is_from_proc_macro(cx, impl_item) {
|
||||
self.check_missing_docs_attrs(cx, impl_item.owner_id.def_id, attrs, impl_item.span, article, desc);
|
||||
}
|
||||
self.prev_span = Some(impl_item.span);
|
||||
}
|
||||
|
||||
fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) {
|
||||
@ -208,6 +253,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field");
|
||||
}
|
||||
}
|
||||
self.prev_span = Some(sf.span);
|
||||
}
|
||||
|
||||
fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) {
|
||||
@ -215,5 +261,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
if !is_from_proc_macro(cx, v) {
|
||||
self.check_missing_docs_attrs(cx, v.def_id, attrs, v.span, "a", "variant");
|
||||
}
|
||||
self.prev_span = Some(v.span);
|
||||
}
|
||||
}
|
||||
|
||||
fn span_to_snippet_contains_docs(cx: &LateContext<'_>, search_span: Span) -> bool {
|
||||
let Some(snippet) = snippet_opt(cx, search_span) else {
|
||||
return false;
|
||||
};
|
||||
snippet.lines().rev().any(|line| line.trim().starts_with("///"))
|
||||
}
|
||||
|
@ -109,7 +109,14 @@ fn collect_unsafe_exprs<'tcx>(
|
||||
ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
hir::Path {
|
||||
res: Res::Def(DefKind::Static{mutability:Mutability::Mut, ..}, _),
|
||||
res:
|
||||
Res::Def(
|
||||
DefKind::Static {
|
||||
mutability: Mutability::Mut,
|
||||
..
|
||||
},
|
||||
_,
|
||||
),
|
||||
..
|
||||
},
|
||||
)) => {
|
||||
@ -149,7 +156,13 @@ fn collect_unsafe_exprs<'tcx>(
|
||||
ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
hir::Path {
|
||||
res: Res::Def(DefKind::Static{mutability:Mutability::Mut, ..}, _),
|
||||
res: Res::Def(
|
||||
DefKind::Static {
|
||||
mutability: Mutability::Mut,
|
||||
..
|
||||
},
|
||||
_
|
||||
),
|
||||
..
|
||||
}
|
||||
))
|
||||
|
@ -1,4 +1,4 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_hir};
|
||||
use clippy_utils::higher;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit;
|
||||
@ -35,9 +35,34 @@ impl<'tcx> LateLintPass<'tcx> for MutMut {
|
||||
}
|
||||
|
||||
fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_>) {
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
if in_external_macro(cx.sess(), ty.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
MutVisitor { cx }.visit_ty(ty);
|
||||
if let hir::TyKind::Ref(
|
||||
_,
|
||||
hir::MutTy {
|
||||
ty: pty,
|
||||
mutbl: hir::Mutability::Mut,
|
||||
},
|
||||
) = ty.kind
|
||||
{
|
||||
if let hir::TyKind::Ref(
|
||||
_,
|
||||
hir::MutTy {
|
||||
mutbl: hir::Mutability::Mut,
|
||||
..
|
||||
},
|
||||
) = pty.kind
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
MUT_MUT,
|
||||
ty.span,
|
||||
"generally you want to avoid `&mut &mut _` if possible",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,17 +87,19 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
|
||||
intravisit::walk_expr(self, body);
|
||||
} else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind {
|
||||
if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind {
|
||||
span_lint(
|
||||
span_lint_hir(
|
||||
self.cx,
|
||||
MUT_MUT,
|
||||
expr.hir_id,
|
||||
expr.span,
|
||||
"generally you want to avoid `&mut &mut _` if possible",
|
||||
);
|
||||
} else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() {
|
||||
if ty.peel_refs().is_sized(self.cx.tcx, self.cx.param_env) {
|
||||
span_lint(
|
||||
span_lint_hir(
|
||||
self.cx,
|
||||
MUT_MUT,
|
||||
expr.hir_id,
|
||||
expr.span,
|
||||
"this expression mutably borrows a mutable reference. Consider reborrowing",
|
||||
);
|
||||
@ -80,37 +107,4 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
|
||||
if in_external_macro(self.cx.sess(), ty.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let hir::TyKind::Ref(
|
||||
_,
|
||||
hir::MutTy {
|
||||
ty: pty,
|
||||
mutbl: hir::Mutability::Mut,
|
||||
},
|
||||
) = ty.kind
|
||||
{
|
||||
if let hir::TyKind::Ref(
|
||||
_,
|
||||
hir::MutTy {
|
||||
mutbl: hir::Mutability::Mut,
|
||||
..
|
||||
},
|
||||
) = pty.kind
|
||||
{
|
||||
span_lint(
|
||||
self.cx,
|
||||
MUT_MUT,
|
||||
ty.span,
|
||||
"generally you want to avoid `&mut &mut _` if possible",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
intravisit::walk_ty(self, ty);
|
||||
}
|
||||
}
|
||||
|
@ -290,14 +290,21 @@ impl NonCopyConst {
|
||||
promoted: None,
|
||||
};
|
||||
let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx);
|
||||
let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, rustc_span::DUMMY_SP);
|
||||
let result = cx
|
||||
.tcx
|
||||
.const_eval_global_id_for_typeck(param_env, cid, rustc_span::DUMMY_SP);
|
||||
self.is_value_unfrozen_raw(cx, result, ty)
|
||||
}
|
||||
|
||||
fn is_value_unfrozen_expr<'tcx>(&self, cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
|
||||
let args = cx.typeck_results().node_args(hir_id);
|
||||
|
||||
let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), rustc_span::DUMMY_SP);
|
||||
let result = Self::const_eval_resolve(
|
||||
cx.tcx,
|
||||
cx.param_env,
|
||||
ty::UnevaluatedConst::new(def_id, args),
|
||||
rustc_span::DUMMY_SP,
|
||||
);
|
||||
self.is_value_unfrozen_raw(cx, result, ty)
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
|
||||
use clippy_utils::get_enclosing_block;
|
||||
use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
|
||||
use clippy_utils::source::snippet;
|
||||
@ -77,36 +77,52 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
|
||||
if let Some(expr) = visitor.read_zero_expr {
|
||||
let applicability = Applicability::MaybeIncorrect;
|
||||
match vec_init_kind {
|
||||
VecInitKind::WithConstCapacity(len) => {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
READ_ZERO_BYTE_VEC,
|
||||
expr.span,
|
||||
"reading zero byte data to `Vec`",
|
||||
"try",
|
||||
format!("{}.resize({len}, 0); {}", ident.as_str(), snippet(cx, expr.span, "..")),
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
VecInitKind::WithConstCapacity(len) => span_lint_hir_and_then(
|
||||
cx,
|
||||
READ_ZERO_BYTE_VEC,
|
||||
expr.hir_id,
|
||||
expr.span,
|
||||
"reading zero byte data to `Vec`",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"try",
|
||||
format!("{}.resize({len}, 0); {}", ident.as_str(), snippet(cx, expr.span, "..")),
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
),
|
||||
VecInitKind::WithExprCapacity(hir_id) => {
|
||||
let e = cx.tcx.hir().expect_expr(hir_id);
|
||||
span_lint_and_sugg(
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
READ_ZERO_BYTE_VEC,
|
||||
expr.hir_id,
|
||||
expr.span,
|
||||
"reading zero byte data to `Vec`",
|
||||
"try",
|
||||
format!(
|
||||
"{}.resize({}, 0); {}",
|
||||
ident.as_str(),
|
||||
snippet(cx, e.span, ".."),
|
||||
snippet(cx, expr.span, "..")
|
||||
),
|
||||
applicability,
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"try",
|
||||
format!(
|
||||
"{}.resize({}, 0); {}",
|
||||
ident.as_str(),
|
||||
snippet(cx, e.span, ".."),
|
||||
snippet(cx, expr.span, "..")
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
_ => {
|
||||
span_lint(cx, READ_ZERO_BYTE_VEC, expr.span, "reading zero byte data to `Vec`");
|
||||
span_lint_hir(
|
||||
cx,
|
||||
READ_ZERO_BYTE_VEC,
|
||||
expr.hir_id,
|
||||
expr.span,
|
||||
"reading zero byte data to `Vec`",
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::rustc_lint::LintContext;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir};
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use hir::Param;
|
||||
@ -273,9 +273,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
|
||||
&& ident == path.segments[0].ident
|
||||
&& count_closure_usage(cx, block, path) == 1
|
||||
{
|
||||
span_lint(
|
||||
span_lint_hir(
|
||||
cx,
|
||||
REDUNDANT_CLOSURE_CALL,
|
||||
second.hir_id,
|
||||
second.span,
|
||||
"closure called just once immediately after it was declared",
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
||||
use clippy_utils::source::{snippet_opt, snippet_with_context};
|
||||
use clippy_utils::sugg::has_enclosing_paren;
|
||||
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
|
||||
@ -380,7 +380,7 @@ fn check_final_expr<'tcx>(
|
||||
return;
|
||||
}
|
||||
|
||||
emit_return_lint(cx, ret_span, semi_spans, &replacement);
|
||||
emit_return_lint(cx, ret_span, semi_spans, &replacement, expr.hir_id);
|
||||
},
|
||||
ExprKind::If(_, then, else_clause_opt) => {
|
||||
check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone());
|
||||
@ -415,18 +415,31 @@ fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool {
|
||||
contains_if(expr, false)
|
||||
}
|
||||
|
||||
fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>, replacement: &RetReplacement<'_>) {
|
||||
fn emit_return_lint(
|
||||
cx: &LateContext<'_>,
|
||||
ret_span: Span,
|
||||
semi_spans: Vec<Span>,
|
||||
replacement: &RetReplacement<'_>,
|
||||
at: HirId,
|
||||
) {
|
||||
if ret_span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
|
||||
let suggestions = std::iter::once((ret_span, replacement.to_string()))
|
||||
.chain(semi_spans.into_iter().map(|span| (span, String::new())))
|
||||
.collect();
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
NEEDLESS_RETURN,
|
||||
at,
|
||||
ret_span,
|
||||
"unneeded `return` statement",
|
||||
|diag| {
|
||||
let suggestions = std::iter::once((ret_span, replacement.to_string()))
|
||||
.chain(semi_spans.into_iter().map(|span| (span, String::new())))
|
||||
.collect();
|
||||
|
||||
diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability());
|
||||
});
|
||||
diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
@ -452,8 +465,8 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)
|
||||
// Go backwards while encountering whitespace and extend the given Span to that point.
|
||||
fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span {
|
||||
if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) {
|
||||
let ws = [' ', '\t', '\n'];
|
||||
if let Some(non_ws_pos) = prev_source.rfind(|c| !ws.contains(&c)) {
|
||||
let ws = [b' ', b'\t', b'\n'];
|
||||
if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) {
|
||||
let len = prev_source.len() - non_ws_pos - 1;
|
||||
return sp.with_lo(sp.lo() - BytePos::from_usize(len));
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
|
||||
sym::core => (STD_INSTEAD_OF_CORE, "std", "core"),
|
||||
sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"),
|
||||
_ => {
|
||||
self.prev_span = first_segment.ident.span;
|
||||
return;
|
||||
},
|
||||
},
|
||||
@ -116,6 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
|
||||
if cx.tcx.crate_name(def_id.krate) == sym::core {
|
||||
(ALLOC_INSTEAD_OF_CORE, "alloc", "core")
|
||||
} else {
|
||||
self.prev_span = first_segment.ident.span;
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
@ -37,7 +37,7 @@ declare_clippy_lint! {
|
||||
/// static BUF: String = const { String::new() };
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
#[clippy::version = "1.77.0"]
|
||||
pub THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST,
|
||||
perf,
|
||||
"suggest using `const` in `thread_local!` macro"
|
||||
|
@ -514,7 +514,7 @@ declare_clippy_lint! {
|
||||
/// ^^^^ ^^ `bool::then` only executes the closure if the condition is true!
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
#[clippy::version = "1.77.0"]
|
||||
pub EAGER_TRANSMUTE,
|
||||
correctness,
|
||||
"eager evaluation of `transmute`"
|
||||
|
@ -392,6 +392,10 @@ impl<'tcx> LateLintPass<'tcx> for Types {
|
||||
}
|
||||
|
||||
fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &hir::FieldDef<'tcx>) {
|
||||
if field.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
let is_exported = cx.effective_visibilities.is_exported(field.def_id);
|
||||
|
||||
self.check_ty(
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{expr_or_init, get_trait_def_id, path_def_id};
|
||||
use clippy_utils::{expr_or_init, fn_def_id_with_node_args, path_def_id};
|
||||
use rustc_ast::BinOpKind;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir as hir;
|
||||
@ -19,11 +19,11 @@ use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks that there isn't an infinite recursion in `PartialEq` trait
|
||||
/// implementation.
|
||||
/// Checks that there isn't an infinite recursion in trait
|
||||
/// implementations.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is a hard to find infinite recursion which will crashing any code
|
||||
/// This is a hard to find infinite recursion that will crash any code.
|
||||
/// using it.
|
||||
///
|
||||
/// ### Example
|
||||
@ -42,7 +42,7 @@ declare_clippy_lint! {
|
||||
/// Use instead:
|
||||
///
|
||||
/// In such cases, either use `#[derive(PartialEq)]` or don't implement it.
|
||||
#[clippy::version = "1.76.0"]
|
||||
#[clippy::version = "1.77.0"]
|
||||
pub UNCONDITIONAL_RECURSION,
|
||||
suspicious,
|
||||
"detect unconditional recursion in some traits implementation"
|
||||
@ -201,7 +201,6 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_def_path)]
|
||||
fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, name: Ident, expr: &Expr<'_>) {
|
||||
let args = cx
|
||||
.tcx
|
||||
@ -224,7 +223,7 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local
|
||||
&& let Some(trait_) = impl_.of_trait
|
||||
&& let Some(trait_def_id) = trait_.trait_def_id()
|
||||
// The trait is `ToString`.
|
||||
&& Some(trait_def_id) == get_trait_def_id(cx, &["alloc", "string", "ToString"])
|
||||
&& cx.tcx.is_diagnostic_item(sym::ToString, trait_def_id)
|
||||
{
|
||||
let is_bad = match expr.kind {
|
||||
ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => {
|
||||
@ -291,7 +290,6 @@ where
|
||||
self.map
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_def_path)]
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
if self.found_default_call {
|
||||
return;
|
||||
@ -303,7 +301,7 @@ where
|
||||
&& is_default_method_on_current_ty(self.cx.tcx, qpath, self.implemented_ty_id)
|
||||
&& let Some(method_def_id) = path_def_id(self.cx, f)
|
||||
&& let Some(trait_def_id) = self.cx.tcx.trait_of_item(method_def_id)
|
||||
&& Some(trait_def_id) == get_trait_def_id(self.cx, &["core", "default", "Default"])
|
||||
&& self.cx.tcx.is_diagnostic_item(sym::Default, trait_def_id)
|
||||
{
|
||||
self.found_default_call = true;
|
||||
span_error(self.cx, self.method_span, expr);
|
||||
@ -312,10 +310,9 @@ where
|
||||
}
|
||||
|
||||
impl UnconditionalRecursion {
|
||||
#[allow(clippy::unnecessary_def_path)]
|
||||
fn init_default_impl_for_type_if_needed(&mut self, cx: &LateContext<'_>) {
|
||||
if self.default_impl_for_type.is_empty()
|
||||
&& let Some(default_trait_id) = get_trait_def_id(cx, &["core", "default", "Default"])
|
||||
&& let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
|
||||
{
|
||||
let impls = cx.tcx.trait_impls_of(default_trait_id);
|
||||
for (ty, impl_def_ids) in impls.non_blanket_impls() {
|
||||
@ -394,6 +391,34 @@ impl UnconditionalRecursion {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_from(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, expr: &Expr<'_>) {
|
||||
let Some(sig) = cx
|
||||
.typeck_results()
|
||||
.liberated_fn_sigs()
|
||||
.get(cx.tcx.local_def_id_to_hir_id(method_def_id))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Check if we are calling `Into::into` where the node args match with our `From::from` signature:
|
||||
// From::from signature: fn(S1) -> S2
|
||||
// <S1 as Into<S2>>::into(s1), node_args=[S1, S2]
|
||||
// If they do match, then it must mean that it is the blanket impl,
|
||||
// which calls back into our `From::from` again (`Into` is not specializable).
|
||||
// rustc's unconditional_recursion already catches calling `From::from` directly
|
||||
if let Some((fn_def_id, node_args)) = fn_def_id_with_node_args(cx, expr)
|
||||
&& let [s1, s2] = **node_args
|
||||
&& let (Some(s1), Some(s2)) = (s1.as_type(), s2.as_type())
|
||||
&& let Some(trait_def_id) = cx.tcx.trait_of_item(fn_def_id)
|
||||
&& cx.tcx.is_diagnostic_item(sym::Into, trait_def_id)
|
||||
&& get_impl_trait_def_id(cx, method_def_id) == cx.tcx.get_diagnostic_item(sym::From)
|
||||
&& s1 == sig.inputs()[0]
|
||||
&& s2 == sig.output()
|
||||
{
|
||||
span_error(cx, method_span, expr);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
@ -410,10 +435,11 @@ impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
|
||||
// Doesn't have a conditional return.
|
||||
&& !has_conditional_return(body, expr)
|
||||
{
|
||||
if name.name == sym::eq || name.name == sym::ne {
|
||||
check_partial_eq(cx, method_span, method_def_id, name, expr);
|
||||
} else if name.name == sym::to_string {
|
||||
check_to_string(cx, method_span, method_def_id, name, expr);
|
||||
match name.name {
|
||||
sym::eq | sym::ne => check_partial_eq(cx, method_span, method_def_id, name, expr),
|
||||
sym::to_string => check_to_string(cx, method_span, method_def_id, name, expr),
|
||||
sym::from => check_from(cx, method_span, method_def_id, expr),
|
||||
_ => {},
|
||||
}
|
||||
self.check_default_new(cx, decl, body, method_span, method_def_id);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths, peel_blocks};
|
||||
use hir::{ExprKind, PatKind};
|
||||
use hir::{ExprKind, HirId, PatKind};
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
@ -135,22 +135,22 @@ fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) {
|
||||
&& is_ok_wild_or_dotdot_pattern(cx, pat)
|
||||
&& let Some(op) = should_lint(cx, init) =>
|
||||
{
|
||||
emit_lint(cx, cond.span, op, &[pat.span]);
|
||||
emit_lint(cx, cond.span, cond.hir_id, op, &[pat.span]);
|
||||
},
|
||||
// we will capture only the case where the match is Ok( ) or Err( )
|
||||
// prefer to match the minimum possible, and expand later if needed
|
||||
// to avoid false positives on something as used as this
|
||||
hir::ExprKind::Match(expr, [arm1, arm2], hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => {
|
||||
if non_consuming_ok_arm(cx, arm1) && non_consuming_err_arm(cx, arm2) {
|
||||
emit_lint(cx, expr.span, op, &[arm1.pat.span]);
|
||||
emit_lint(cx, expr.span, expr.hir_id, op, &[arm1.pat.span]);
|
||||
}
|
||||
if non_consuming_ok_arm(cx, arm2) && non_consuming_err_arm(cx, arm1) {
|
||||
emit_lint(cx, expr.span, op, &[arm2.pat.span]);
|
||||
emit_lint(cx, expr.span, expr.hir_id, op, &[arm2.pat.span]);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Match(_, _, hir::MatchSource::Normal) => {},
|
||||
_ if let Some(op) = should_lint(cx, expr) => {
|
||||
emit_lint(cx, expr.span, op, &[]);
|
||||
emit_lint(cx, expr.span, expr.hir_id, op, &[]);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
@ -279,7 +279,7 @@ fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option<IoOp> {
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_lint(cx: &LateContext<'_>, span: Span, op: IoOp, wild_cards: &[Span]) {
|
||||
fn emit_lint(cx: &LateContext<'_>, span: Span, at: HirId, op: IoOp, wild_cards: &[Span]) {
|
||||
let (msg, help) = match op {
|
||||
IoOp::AsyncRead(false) => (
|
||||
"read amount is not handled",
|
||||
@ -301,7 +301,7 @@ fn emit_lint(cx: &LateContext<'_>, span: Span, op: IoOp, wild_cards: &[Span]) {
|
||||
IoOp::SyncWrite(true) | IoOp::AsyncWrite(true) => ("written amount is not handled", None),
|
||||
};
|
||||
|
||||
span_lint_and_then(cx, UNUSED_IO_AMOUNT, span, msg, |diag| {
|
||||
span_lint_hir_and_then(cx, UNUSED_IO_AMOUNT, at, span, msg, |diag| {
|
||||
if let Some(help_str) = help {
|
||||
diag.help(help_str);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
|
||||
use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators};
|
||||
use rustc_ast::Mutability;
|
||||
@ -79,13 +79,15 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
|
||||
}
|
||||
|
||||
if !vis.found_peek_call {
|
||||
span_lint_and_help(
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
UNUSED_PEEKABLE,
|
||||
local.hir_id,
|
||||
ident.span,
|
||||
"`peek` never called on `Peekable` iterator",
|
||||
None,
|
||||
"consider removing the call to `peekable`",
|
||||
|diag| {
|
||||
diag.help("consider removing the call to `peekable`");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,12 @@ use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit::{walk_inf, walk_ty, Visitor};
|
||||
use rustc_hir::{
|
||||
self as hir, Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericArgsParentheses, GenericParam, GenericParamKind,
|
||||
HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
|
||||
self as hir, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl,
|
||||
ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
|
||||
};
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::Ty as MiddleTy;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
@ -95,10 +96,9 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
||||
let stack_item = if let ItemKind::Impl(Impl { self_ty, generics, .. }) = item.kind
|
||||
&& let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind
|
||||
&& let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args
|
||||
&& parameters.as_ref().map_or(true, |params| {
|
||||
params.parenthesized == GenericArgsParentheses::No
|
||||
&& !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
|
||||
})
|
||||
&& parameters
|
||||
.as_ref()
|
||||
.map_or(true, |params| params.parenthesized == GenericArgsParentheses::No)
|
||||
&& !item.span.from_expansion()
|
||||
&& !is_from_proc_macro(cx, item)
|
||||
// expensive, should be last check
|
||||
@ -226,7 +226,12 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
||||
} else {
|
||||
hir_ty_to_ty(cx.tcx, hir_ty)
|
||||
}
|
||||
&& same_type_and_consts(ty, cx.tcx.type_of(impl_id).instantiate_identity())
|
||||
&& let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity()
|
||||
&& same_type_and_consts(ty, impl_ty)
|
||||
// Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that
|
||||
// the lifetime parameters of `ty` are ellided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in
|
||||
// which case we must still trigger the lint.
|
||||
&& (has_no_lifetime(ty) || same_lifetimes(ty, impl_ty))
|
||||
{
|
||||
span_lint(cx, hir_ty.span);
|
||||
}
|
||||
@ -318,3 +323,37 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
|
||||
span_lint(cx, span);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if types `a` and `b` have the same lifetime parameters, otherwise returns
|
||||
/// `false`.
|
||||
///
|
||||
/// This function does not check that types `a` and `b` are the same types.
|
||||
fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool {
|
||||
use rustc_middle::ty::{Adt, GenericArgKind};
|
||||
match (&a.kind(), &b.kind()) {
|
||||
(&Adt(_, args_a), &Adt(_, args_b)) => {
|
||||
args_a
|
||||
.iter()
|
||||
.zip(args_b.iter())
|
||||
.all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) {
|
||||
// TODO: Handle inferred lifetimes
|
||||
(GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b,
|
||||
(GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b),
|
||||
_ => true,
|
||||
})
|
||||
},
|
||||
_ => a == b,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `ty` has no lifetime parameter, otherwise returns `false`.
|
||||
fn has_no_lifetime(ty: MiddleTy<'_>) -> bool {
|
||||
use rustc_middle::ty::{Adt, GenericArgKind};
|
||||
match ty.kind() {
|
||||
&Adt(_, args) => !args
|
||||
.iter()
|
||||
// TODO: Handle inferred lifetimes
|
||||
.any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(..))),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
@ -925,7 +925,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
|
||||
&& let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr))
|
||||
&& match_type(self.cx, expr_ty, &paths::LINT)
|
||||
{
|
||||
if let hir::def::Res::Def(DefKind::Static(..), _) = path.res {
|
||||
if let hir::def::Res::Def(DefKind::Static { .. }, _) = path.res {
|
||||
let lint_name = last_path_segment(qpath).ident.name;
|
||||
self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
|
||||
} else if let Some(local) = get_parent_local(self.cx, expr) {
|
||||
|
@ -223,7 +223,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
|
||||
None
|
||||
}
|
||||
},
|
||||
Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path(
|
||||
Res::Def(DefKind::Static { .. }, def_id) => read_mir_alloc_def_path(
|
||||
cx,
|
||||
cx.tcx.eval_static_initializer(def_id).ok()?.inner(),
|
||||
cx.tcx.type_of(def_id).instantiate_identity(),
|
||||
|
@ -213,7 +213,7 @@ fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool {
|
||||
// Allow skipping imports containing user configured segments,
|
||||
// i.e. "...::utils::...::*" if user put `allowed-wildcard-imports = ["utils"]` in `Clippy.toml`
|
||||
fn is_allowed_via_config(segments: &[PathSegment<'_>], allowed_segments: &FxHashSet<String>) -> bool {
|
||||
// segment matching need to be exact instead of using 'contains', in case user unintentionaly put
|
||||
// segment matching need to be exact instead of using 'contains', in case user unintentionally put
|
||||
// a single character in the config thus skipping most of the warnings.
|
||||
segments.iter().any(|seg| allowed_segments.contains(seg.ident.as_str()))
|
||||
}
|
||||
|
154
clippy_lints/src/zero_repeat_side_effects.rs
Normal file
154
clippy_lints/src/zero_repeat_side_effects.rs
Normal file
@ -0,0 +1,154 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher::VecArgs;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{ExprKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, ConstKind, Ty};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for array or vec initializations which call a function or method,
|
||||
/// but which have a repeat count of zero.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Such an initialization, despite having a repeat length of 0, will still call the inner function.
|
||||
/// This may not be obvious and as such there may be unintended side effects in code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn side_effect() -> i32 {
|
||||
/// println!("side effect");
|
||||
/// 10
|
||||
/// }
|
||||
/// let a = [side_effect(); 0];
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn side_effect() -> i32 {
|
||||
/// println!("side effect");
|
||||
/// 10
|
||||
/// }
|
||||
/// side_effect();
|
||||
/// let a: [i32; 0] = [];
|
||||
/// ```
|
||||
#[clippy::version = "1.75.0"]
|
||||
pub ZERO_REPEAT_SIDE_EFFECTS,
|
||||
suspicious,
|
||||
"usage of zero-sized initializations of arrays or vecs causing side effects"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ZeroRepeatSideEffects => [ZERO_REPEAT_SIDE_EFFECTS]);
|
||||
|
||||
impl LateLintPass<'_> for ZeroRepeatSideEffects {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>) {
|
||||
if let Some(args) = VecArgs::hir(cx, expr)
|
||||
&& let VecArgs::Repeat(inner_expr, len) = args
|
||||
&& let ExprKind::Lit(l) = len.kind
|
||||
&& let LitKind::Int(i, _) = l.node
|
||||
&& i.0 == 0
|
||||
{
|
||||
inner_check(cx, expr, inner_expr, true);
|
||||
} else if let ExprKind::Repeat(inner_expr, _) = expr.kind
|
||||
&& let ty::Array(_, cst) = cx.typeck_results().expr_ty(expr).kind()
|
||||
&& let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
|
||||
&& let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)
|
||||
&& element_count == 0
|
||||
{
|
||||
inner_check(cx, expr, inner_expr, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: &'_ rustc_hir::Expr<'_>, is_vec: bool) {
|
||||
// check if expr is a call or has a call inside it
|
||||
if for_each_expr(inner_expr, |x| {
|
||||
if let ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _) = x.kind {
|
||||
std::ops::ControlFlow::Break(())
|
||||
} else {
|
||||
std::ops::ControlFlow::Continue(())
|
||||
}
|
||||
})
|
||||
.is_some()
|
||||
{
|
||||
let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id);
|
||||
let return_type = cx.typeck_results().expr_ty(expr);
|
||||
|
||||
if let Node::Local(l) = parent_hir_node {
|
||||
array_span_lint(
|
||||
cx,
|
||||
l.span,
|
||||
inner_expr.span,
|
||||
l.pat.span,
|
||||
Some(return_type),
|
||||
is_vec,
|
||||
false,
|
||||
);
|
||||
} else if let Node::Expr(x) = parent_hir_node
|
||||
&& let ExprKind::Assign(l, _, _) = x.kind
|
||||
{
|
||||
array_span_lint(cx, x.span, inner_expr.span, l.span, Some(return_type), is_vec, true);
|
||||
} else {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ZERO_REPEAT_SIDE_EFFECTS,
|
||||
expr.span.source_callsite(),
|
||||
"function or method calls as the initial value in zero-sized array initializers may cause side effects",
|
||||
"consider using",
|
||||
format!(
|
||||
"{{ {}; {}[] as {return_type} }}",
|
||||
snippet(cx, inner_expr.span.source_callsite(), ".."),
|
||||
if is_vec { "vec!" } else { "" },
|
||||
),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn array_span_lint(
|
||||
cx: &LateContext<'_>,
|
||||
expr_span: Span,
|
||||
func_call_span: Span,
|
||||
variable_name_span: Span,
|
||||
expr_ty: Option<Ty<'_>>,
|
||||
is_vec: bool,
|
||||
is_assign: bool,
|
||||
) {
|
||||
let has_ty = expr_ty.is_some();
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ZERO_REPEAT_SIDE_EFFECTS,
|
||||
expr_span.source_callsite(),
|
||||
"function or method calls as the initial value in zero-sized array initializers may cause side effects",
|
||||
"consider using",
|
||||
format!(
|
||||
"{}; {}{}{} = {}[]{}{}",
|
||||
snippet(cx, func_call_span.source_callsite(), ".."),
|
||||
if has_ty && !is_assign { "let " } else { "" },
|
||||
snippet(cx, variable_name_span.source_callsite(), ".."),
|
||||
if let Some(ty) = expr_ty
|
||||
&& !is_assign
|
||||
{
|
||||
format!(": {ty}")
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
if is_vec { "vec!" } else { "" },
|
||||
if let Some(ty) = expr_ty
|
||||
&& is_assign
|
||||
{
|
||||
format!(" as {ty}")
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
if is_assign { "" } else { ";" }
|
||||
),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy_utils"
|
||||
version = "0.1.78"
|
||||
version = "0.1.79"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
|
@ -10,8 +10,10 @@ use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item
|
||||
use rustc_lexer::tokenize;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::interpret::{alloc_range, Scalar};
|
||||
use rustc_middle::mir::ConstValue;
|
||||
use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy};
|
||||
use rustc_middle::{bug, mir, span_bug};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
use rustc_span::SyntaxContext;
|
||||
use rustc_target::abi::Size;
|
||||
@ -307,6 +309,12 @@ impl ConstantSource {
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to check whether the expression is a constant representing an empty slice, str, array,
|
||||
/// etc…
|
||||
pub fn constant_is_empty(lcx: &LateContext<'_>, e: &Expr<'_>) -> Option<bool> {
|
||||
ConstEvalLateContext::new(lcx, lcx.typeck_results()).expr_is_empty(e)
|
||||
}
|
||||
|
||||
/// Attempts to evaluate the expression as a constant.
|
||||
pub fn constant<'tcx>(
|
||||
lcx: &LateContext<'tcx>,
|
||||
@ -406,7 +414,13 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||
match e.kind {
|
||||
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value),
|
||||
ExprKind::DropTemps(e) => self.expr(e),
|
||||
ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
|
||||
ExprKind::Path(ref qpath) => {
|
||||
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| {
|
||||
let result = mir_to_const(this.lcx, result)?;
|
||||
this.source = ConstantSource::Constant;
|
||||
Some(result)
|
||||
})
|
||||
},
|
||||
ExprKind::Block(block, _) => self.block(block),
|
||||
ExprKind::Lit(lit) => {
|
||||
if is_direct_expn_of(e.span, "cfg").is_some() {
|
||||
@ -472,6 +486,49 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple constant folding to determine if an expression is an empty slice, str, array, …
|
||||
/// `None` will be returned if the constness cannot be determined, or if the resolution
|
||||
/// leaves the local crate.
|
||||
pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> {
|
||||
match e.kind {
|
||||
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value),
|
||||
ExprKind::DropTemps(e) => self.expr_is_empty(e),
|
||||
ExprKind::Path(ref qpath) => {
|
||||
if !self
|
||||
.typeck_results
|
||||
.qpath_res(qpath, e.hir_id)
|
||||
.opt_def_id()
|
||||
.is_some_and(DefId::is_local)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| {
|
||||
mir_is_empty(this.lcx, result)
|
||||
})
|
||||
},
|
||||
ExprKind::Lit(lit) => {
|
||||
if is_direct_expn_of(e.span, "cfg").is_some() {
|
||||
None
|
||||
} else {
|
||||
match &lit.node {
|
||||
LitKind::Str(is, _) => Some(is.is_empty()),
|
||||
LitKind::ByteStr(s, _) | LitKind::CStr(s, _) => Some(s.is_empty()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()),
|
||||
ExprKind::Repeat(..) => {
|
||||
if let ty::Array(_, n) = self.typeck_results.expr_ty(e).kind() {
|
||||
Some(n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)? == 0)
|
||||
} else {
|
||||
span_bug!(e.span, "typeck error");
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::cast_possible_wrap)]
|
||||
fn constant_not(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option<Constant<'tcx>> {
|
||||
use self::Constant::{Bool, Int};
|
||||
@ -519,8 +576,11 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||
vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
|
||||
}
|
||||
|
||||
/// Lookup a possibly constant expression from an `ExprKind::Path`.
|
||||
fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant<'tcx>> {
|
||||
/// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it.
|
||||
fn fetch_path_and_apply<T, F>(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&mut Self, rustc_middle::mir::Const<'tcx>) -> Option<T>,
|
||||
{
|
||||
let res = self.typeck_results.qpath_res(qpath, id);
|
||||
match res {
|
||||
Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
|
||||
@ -553,9 +613,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||
.const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span())
|
||||
.ok()
|
||||
.map(|val| rustc_middle::mir::Const::from_value(val, ty))?;
|
||||
let result = mir_to_const(self.lcx, result)?;
|
||||
self.source = ConstantSource::Constant;
|
||||
Some(result)
|
||||
f(self, result)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
@ -746,7 +804,6 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||
}
|
||||
|
||||
pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> {
|
||||
use rustc_middle::mir::ConstValue;
|
||||
let mir::Const::Val(val, _) = result else {
|
||||
// We only work on evaluated consts.
|
||||
return None;
|
||||
@ -794,6 +851,42 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
|
||||
}
|
||||
}
|
||||
|
||||
fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<bool> {
|
||||
let mir::Const::Val(val, _) = result else {
|
||||
// We only work on evaluated consts.
|
||||
return None;
|
||||
};
|
||||
match (val, result.ty().kind()) {
|
||||
(_, ty::Ref(_, inner_ty, _)) => match inner_ty.kind() {
|
||||
ty::Str | ty::Slice(_) => {
|
||||
if let ConstValue::Indirect { alloc_id, offset } = val {
|
||||
// Get the length from the slice, using the same formula as
|
||||
// [`ConstValue::try_get_slice_bytes_for_diagnostics`].
|
||||
let a = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
|
||||
let ptr_size = lcx.tcx.data_layout.pointer_size;
|
||||
if a.size() < offset + 2 * ptr_size {
|
||||
// (partially) dangling reference
|
||||
return None;
|
||||
}
|
||||
let len = a
|
||||
.read_scalar(&lcx.tcx, alloc_range(offset + ptr_size, ptr_size), false)
|
||||
.ok()?
|
||||
.to_target_usize(&lcx.tcx)
|
||||
.ok()?;
|
||||
Some(len == 0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
ty::Array(_, len) => Some(len.try_to_target_usize(lcx.tcx)? == 0),
|
||||
_ => None,
|
||||
},
|
||||
(ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(lcx.tcx)? == 0),
|
||||
(ConstValue::ZeroSized, _) => Some(true),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn field_of_struct<'tcx>(
|
||||
adt_def: ty::AdtDef<'tcx>,
|
||||
lcx: &LateContext<'tcx>,
|
||||
|
@ -36,6 +36,20 @@ fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) {
|
||||
/// Usually it's nicer to provide more context for lint messages.
|
||||
/// Be sure the output is understandable when you use this method.
|
||||
///
|
||||
/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine
|
||||
/// the lint level.
|
||||
/// For the `span_lint` function, the node that was passed into the `LintPass::check_*` function is
|
||||
/// used.
|
||||
///
|
||||
/// If you're emitting the lint at the span of a different node than the one provided by the
|
||||
/// `LintPass::check_*` function, consider using [`span_lint_hir`] instead.
|
||||
/// This is needed for `#[allow]` and `#[expect]` attributes to work on the node
|
||||
/// highlighted in the displayed warning.
|
||||
///
|
||||
/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works
|
||||
/// where you would expect it to.
|
||||
/// If it doesn't, you likely need to use [`span_lint_hir`] instead.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
@ -61,6 +75,20 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
|
||||
///
|
||||
/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
|
||||
///
|
||||
/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine
|
||||
/// the lint level.
|
||||
/// For the `span_lint_and_help` function, the node that was passed into the `LintPass::check_*`
|
||||
/// function is used.
|
||||
///
|
||||
/// If you're emitting the lint at the span of a different node than the one provided by the
|
||||
/// `LintPass::check_*` function, consider using [`span_lint_hir_and_then`] instead.
|
||||
/// This is needed for `#[allow]` and `#[expect]` attributes to work on the node
|
||||
/// highlighted in the displayed warning.
|
||||
///
|
||||
/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works
|
||||
/// where you would expect it to.
|
||||
/// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```text
|
||||
@ -99,6 +127,20 @@ pub fn span_lint_and_help<T: LintContext>(
|
||||
///
|
||||
/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
|
||||
///
|
||||
/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine
|
||||
/// the lint level.
|
||||
/// For the `span_lint_and_note` function, the node that was passed into the `LintPass::check_*`
|
||||
/// function is used.
|
||||
///
|
||||
/// If you're emitting the lint at the span of a different node than the one provided by the
|
||||
/// `LintPass::check_*` function, consider using [`span_lint_hir_and_then`] instead.
|
||||
/// This is needed for `#[allow]` and `#[expect]` attributes to work on the node
|
||||
/// highlighted in the displayed warning.
|
||||
///
|
||||
/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works
|
||||
/// where you would expect it to.
|
||||
/// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```text
|
||||
@ -139,6 +181,20 @@ pub fn span_lint_and_note<T: LintContext>(
|
||||
///
|
||||
/// If you need to customize your lint output a lot, use this function.
|
||||
/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
|
||||
///
|
||||
/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine
|
||||
/// the lint level.
|
||||
/// For the `span_lint_and_then` function, the node that was passed into the `LintPass::check_*`
|
||||
/// function is used.
|
||||
///
|
||||
/// If you're emitting the lint at the span of a different node than the one provided by the
|
||||
/// `LintPass::check_*` function, consider using [`span_lint_hir_and_then`] instead.
|
||||
/// This is needed for `#[allow]` and `#[expect]` attributes to work on the node
|
||||
/// highlighted in the displayed warning.
|
||||
///
|
||||
/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works
|
||||
/// where you would expect it to.
|
||||
/// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead.
|
||||
pub fn span_lint_and_then<C, S, F>(cx: &C, lint: &'static Lint, sp: S, msg: &str, f: F)
|
||||
where
|
||||
C: LintContext,
|
||||
@ -152,6 +208,30 @@ where
|
||||
});
|
||||
}
|
||||
|
||||
/// Like [`span_lint`], but emits the lint at the node identified by the given `HirId`.
|
||||
///
|
||||
/// This is in contrast to [`span_lint`], which always emits the lint at the node that was last
|
||||
/// passed to the `LintPass::check_*` function.
|
||||
///
|
||||
/// The `HirId` is used for checking lint level attributes and to fulfill lint expectations defined
|
||||
/// via the `#[expect]` attribute.
|
||||
///
|
||||
/// For example:
|
||||
/// ```ignore
|
||||
/// fn f() { /* <node_1> */
|
||||
///
|
||||
/// #[allow(clippy::some_lint)]
|
||||
/// let _x = /* <expr_1> */;
|
||||
/// }
|
||||
/// ```
|
||||
/// If `some_lint` does its analysis in `LintPass::check_fn` (at `<node_1>`) and emits a lint at
|
||||
/// `<expr_1>` using [`span_lint`], then allowing the lint at `<expr_1>` as attempted in the snippet
|
||||
/// will not work!
|
||||
/// Even though that is where the warning points at, which would be confusing to users.
|
||||
///
|
||||
/// Instead, use this function and also pass the `HirId` of `<expr_1>`, which will let
|
||||
/// the compiler check lint level attributes at the place of the expression and
|
||||
/// the `#[allow]` will work.
|
||||
pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) {
|
||||
#[expect(clippy::disallowed_methods)]
|
||||
cx.tcx.node_span_lint(lint, hir_id, sp, msg.to_string(), |diag| {
|
||||
@ -159,6 +239,30 @@ pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, s
|
||||
});
|
||||
}
|
||||
|
||||
/// Like [`span_lint_and_then`], but emits the lint at the node identified by the given `HirId`.
|
||||
///
|
||||
/// This is in contrast to [`span_lint_and_then`], which always emits the lint at the node that was
|
||||
/// last passed to the `LintPass::check_*` function.
|
||||
///
|
||||
/// The `HirId` is used for checking lint level attributes and to fulfill lint expectations defined
|
||||
/// via the `#[expect]` attribute.
|
||||
///
|
||||
/// For example:
|
||||
/// ```ignore
|
||||
/// fn f() { /* <node_1> */
|
||||
///
|
||||
/// #[allow(clippy::some_lint)]
|
||||
/// let _x = /* <expr_1> */;
|
||||
/// }
|
||||
/// ```
|
||||
/// If `some_lint` does its analysis in `LintPass::check_fn` (at `<node_1>`) and emits a lint at
|
||||
/// `<expr_1>` using [`span_lint`], then allowing the lint at `<expr_1>` as attempted in the snippet
|
||||
/// will not work!
|
||||
/// Even though that is where the warning points at, which would be confusing to users.
|
||||
///
|
||||
/// Instead, use this function and also pass the `HirId` of `<expr_1>`, which will let
|
||||
/// the compiler check lint level attributes at the place of the expression and
|
||||
/// the `#[allow]` will work.
|
||||
pub fn span_lint_hir_and_then(
|
||||
cx: &LateContext<'_>,
|
||||
lint: &'static Lint,
|
||||
@ -182,6 +286,20 @@ pub fn span_lint_hir_and_then(
|
||||
///
|
||||
/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
|
||||
///
|
||||
/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine
|
||||
/// the lint level.
|
||||
/// For the `span_lint_and_sugg` function, the node that was passed into the `LintPass::check_*`
|
||||
/// function is used.
|
||||
///
|
||||
/// If you're emitting the lint at the span of a different node than the one provided by the
|
||||
/// `LintPass::check_*` function, consider using [`span_lint_hir_and_then`] instead.
|
||||
/// This is needed for `#[allow]` and `#[expect]` attributes to work on the node
|
||||
/// highlighted in the displayed warning.
|
||||
///
|
||||
/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works
|
||||
/// where you would expect it to.
|
||||
/// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```text
|
||||
|
@ -2246,8 +2246,21 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
|
||||
|
||||
/// Returns the `DefId` of the callee if the given expression is a function or method call.
|
||||
pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
|
||||
fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
|
||||
}
|
||||
|
||||
/// Returns the `DefId` of the callee if the given expression is a function or method call,
|
||||
/// as well as its node args.
|
||||
pub fn fn_def_id_with_node_args<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &Expr<'_>,
|
||||
) -> Option<(DefId, rustc_ty::GenericArgsRef<'tcx>)> {
|
||||
let typeck = cx.typeck_results();
|
||||
match &expr.kind {
|
||||
ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
|
||||
ExprKind::MethodCall(..) => Some((
|
||||
typeck.type_dependent_def_id(expr.hir_id)?,
|
||||
typeck.node_args(expr.hir_id),
|
||||
)),
|
||||
ExprKind::Call(
|
||||
Expr {
|
||||
kind: ExprKind::Path(qpath),
|
||||
@ -2259,9 +2272,9 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
|
||||
// Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or
|
||||
// deref to fn pointers, dyn Fn, impl Fn - #8850
|
||||
if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
|
||||
cx.typeck_results().qpath_res(qpath, *path_hir_id)
|
||||
typeck.qpath_res(qpath, *path_hir_id)
|
||||
{
|
||||
Some(id)
|
||||
Some((id, typeck.node_args(*path_hir_id)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "B
|
||||
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
|
||||
pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
|
||||
pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
|
||||
pub const CORE_ITER_ENUMERATE_METHOD: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "enumerate"];
|
||||
pub const CORE_ITER_ENUMERATE_STRUCT: [&str; 5] = ["core", "iter", "adapters", "enumerate", "Enumerate"];
|
||||
pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
|
||||
pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"];
|
||||
pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "declare_clippy_lint"
|
||||
version = "0.1.78"
|
||||
version = "0.1.79"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2024-03-07"
|
||||
channel = "nightly-2024-03-21"
|
||||
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||
|
@ -4,6 +4,7 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint`
|
||||
LL | cx.span_lint(lint, span, msg, |_| {});
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead (from clippy.toml)
|
||||
= note: `-D clippy::disallowed-methods` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]`
|
||||
|
||||
@ -12,6 +13,8 @@ error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_
|
||||
|
|
||||
LL | tcx.node_span_lint(lint, hir_id, span, msg, |_| {});
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead (from clippy.toml)
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
38
tests/ui-toml/dbg_macro/dbg_macro.fixed
Normal file
38
tests/ui-toml/dbg_macro/dbg_macro.fixed
Normal file
@ -0,0 +1,38 @@
|
||||
//@compile-flags: --test
|
||||
#![warn(clippy::dbg_macro)]
|
||||
#![allow(clippy::unnecessary_operation, clippy::no_effect)]
|
||||
|
||||
fn foo(n: u32) -> u32 {
|
||||
if let Some(n) = n.checked_sub(4) { n } else { n }
|
||||
}
|
||||
|
||||
fn factorial(n: u32) -> u32 {
|
||||
if n <= 1 {
|
||||
1
|
||||
} else {
|
||||
n * factorial(n - 1)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
42;
|
||||
foo(3) + factorial(4);
|
||||
(1, 2, 3, 4, 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn issue8481() {
|
||||
dbg!(1);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn foo2() {
|
||||
dbg!(1);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod mod1 {
|
||||
fn func() {
|
||||
dbg!(1);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
//@compile-flags: --test
|
||||
#![warn(clippy::dbg_macro)]
|
||||
//@no-rustfix
|
||||
#![allow(clippy::unnecessary_operation, clippy::no_effect)]
|
||||
|
||||
fn foo(n: u32) -> u32 {
|
||||
if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
|
||||
}
|
||||
@ -15,9 +16,7 @@ fn factorial(n: u32) -> u32 {
|
||||
|
||||
fn main() {
|
||||
dbg!(42);
|
||||
dbg!(dbg!(dbg!(42)));
|
||||
foo(3) + dbg!(factorial(4));
|
||||
dbg!(1, 2, dbg!(3, 4));
|
||||
dbg!(1, 2, 3, 4, 5);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:5:22
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:6:22
|
||||
|
|
||||
LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -12,7 +12,7 @@ LL | if let Some(n) = n.checked_sub(4) { n } else { n }
|
||||
| ~~~~~~~~~~~~~~~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:9:8
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:10:8
|
||||
|
|
||||
LL | if dbg!(n <= 1) {
|
||||
| ^^^^^^^^^^^^
|
||||
@ -23,7 +23,7 @@ LL | if n <= 1 {
|
||||
| ~~~~~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:10:9
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:11:9
|
||||
|
|
||||
LL | dbg!(1)
|
||||
| ^^^^^^^
|
||||
@ -34,7 +34,7 @@ LL | 1
|
||||
|
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:12:9
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:13:9
|
||||
|
|
||||
LL | dbg!(n * factorial(n - 1))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -45,7 +45,7 @@ LL | n * factorial(n - 1)
|
||||
|
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:17:5
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:18:5
|
||||
|
|
||||
LL | dbg!(42);
|
||||
| ^^^^^^^^
|
||||
@ -55,17 +55,6 @@ help: remove the invocation before committing it to a version control system
|
||||
LL | 42;
|
||||
| ~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:18:5
|
||||
|
|
||||
LL | dbg!(dbg!(dbg!(42)));
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL | dbg!(dbg!(42));
|
||||
| ~~~~~~~~~~~~~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:19:14
|
||||
|
|
||||
@ -80,17 +69,6 @@ LL | foo(3) + factorial(4);
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:20:5
|
||||
|
|
||||
LL | dbg!(1, 2, dbg!(3, 4));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL | (1, 2, dbg!(3, 4));
|
||||
| ~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui-toml/dbg_macro/dbg_macro.rs:21:5
|
||||
|
|
||||
LL | dbg!(1, 2, 3, 4, 5);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
@ -99,5 +77,5 @@ help: remove the invocation before committing it to a version control system
|
||||
LL | (1, 2, 3, 4, 5);
|
||||
| ~~~~~~~~~~~~~~~
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
//@aux-build:proc_macros.rs
|
||||
#![feature(lint_reasons)]
|
||||
#![deny(clippy::allow_attributes_without_reason)]
|
||||
#![allow(unfulfilled_lint_expectations)]
|
||||
#![allow(unfulfilled_lint_expectations, clippy::duplicated_attributes)]
|
||||
|
||||
extern crate proc_macros;
|
||||
use proc_macros::{external, with_span};
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: `allow` attribute without specifying a reason
|
||||
--> tests/ui/allow_attributes_without_reason.rs:4:1
|
||||
|
|
||||
LL | #![allow(unfulfilled_lint_expectations)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | #![allow(unfulfilled_lint_expectations, clippy::duplicated_attributes)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: try adding a reason at the end with `, reason = ".."`
|
||||
note: the lint level is defined here
|
||||
|
@ -128,6 +128,19 @@ fn ignore_generic_clone<T: Clone>(a: &mut T, b: &T) {
|
||||
*a = b.clone();
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.62"]
|
||||
fn msrv_1_62(mut a: String, b: String, c: &str) {
|
||||
a.clone_from(&b);
|
||||
// Should not be linted, as clone_into wasn't stabilized until 1.63
|
||||
a = c.to_owned();
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.63"]
|
||||
fn msrv_1_63(mut a: String, b: String, c: &str) {
|
||||
a.clone_from(&b);
|
||||
c.clone_into(&mut a);
|
||||
}
|
||||
|
||||
macro_rules! clone_inside {
|
||||
($a:expr, $b: expr) => {
|
||||
$a = $b.clone();
|
||||
|
@ -128,6 +128,19 @@ fn ignore_generic_clone<T: Clone>(a: &mut T, b: &T) {
|
||||
*a = b.clone();
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.62"]
|
||||
fn msrv_1_62(mut a: String, b: String, c: &str) {
|
||||
a = b.clone();
|
||||
// Should not be linted, as clone_into wasn't stabilized until 1.63
|
||||
a = c.to_owned();
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.63"]
|
||||
fn msrv_1_63(mut a: String, b: String, c: &str) {
|
||||
a = b.clone();
|
||||
a = c.to_owned();
|
||||
}
|
||||
|
||||
macro_rules! clone_inside {
|
||||
($a:expr, $b: expr) => {
|
||||
$a = $b.clone();
|
||||
|
@ -67,41 +67,59 @@ error: assigning the result of `Clone::clone()` may be inefficient
|
||||
LL | a = b.clone();
|
||||
| ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
|
||||
|
||||
error: assigning the result of `Clone::clone()` may be inefficient
|
||||
--> tests/ui/assigning_clones.rs:133:5
|
||||
|
|
||||
LL | a = b.clone();
|
||||
| ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
|
||||
|
||||
error: assigning the result of `Clone::clone()` may be inefficient
|
||||
--> tests/ui/assigning_clones.rs:140:5
|
||||
|
|
||||
LL | a = b.clone();
|
||||
| ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
|
||||
|
||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||
--> tests/ui/assigning_clones.rs:145:5
|
||||
--> tests/ui/assigning_clones.rs:141:5
|
||||
|
|
||||
LL | a = c.to_owned();
|
||||
| ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)`
|
||||
|
||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||
--> tests/ui/assigning_clones.rs:158:5
|
||||
|
|
||||
LL | *mut_string = ref_str.to_owned();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)`
|
||||
|
||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||
--> tests/ui/assigning_clones.rs:149:5
|
||||
--> tests/ui/assigning_clones.rs:162:5
|
||||
|
|
||||
LL | mut_string = ref_str.to_owned();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)`
|
||||
|
||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||
--> tests/ui/assigning_clones.rs:170:5
|
||||
--> tests/ui/assigning_clones.rs:183:5
|
||||
|
|
||||
LL | **mut_box_string = ref_str.to_owned();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))`
|
||||
|
||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||
--> tests/ui/assigning_clones.rs:174:5
|
||||
--> tests/ui/assigning_clones.rs:187:5
|
||||
|
|
||||
LL | **mut_box_string = ref_str.to_owned();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))`
|
||||
|
||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||
--> tests/ui/assigning_clones.rs:178:5
|
||||
--> tests/ui/assigning_clones.rs:191:5
|
||||
|
|
||||
LL | *mut_thing = ToOwned::to_owned(ref_str);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)`
|
||||
|
||||
error: assigning the result of `ToOwned::to_owned()` may be inefficient
|
||||
--> tests/ui/assigning_clones.rs:182:5
|
||||
--> tests/ui/assigning_clones.rs:195:5
|
||||
|
|
||||
LL | mut_thing = ToOwned::to_owned(ref_str);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)`
|
||||
|
||||
error: aborting due to 17 previous errors
|
||||
error: aborting due to 20 previous errors
|
||||
|
||||
|
@ -11,8 +11,8 @@ use quote::{quote, quote_spanned};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::token::Star;
|
||||
use syn::{
|
||||
parse_macro_input, parse_quote, FnArg, ImplItem, ItemFn, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType,
|
||||
Signature, TraitItem, Type,
|
||||
parse_macro_input, parse_quote, FnArg, ImplItem, ItemFn, ItemImpl, ItemStruct, ItemTrait, Lifetime, Pat, PatIdent,
|
||||
PatType, Signature, TraitItem, Type, Visibility,
|
||||
};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
@ -101,9 +101,7 @@ pub fn fake_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let mut item = parse_macro_input!(item as ItemFn);
|
||||
let span = item.block.brace_token.span;
|
||||
|
||||
if item.sig.asyncness.is_some() {
|
||||
item.sig.asyncness = None;
|
||||
}
|
||||
item.sig.asyncness = None;
|
||||
|
||||
let crate_name = quote! { fake_crate };
|
||||
let block = item.block;
|
||||
@ -128,7 +126,7 @@ pub fn fake_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn fake_desugar_await(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mut async_fn = syn::parse_macro_input!(input as syn::ItemFn);
|
||||
let mut async_fn = parse_macro_input!(input as syn::ItemFn);
|
||||
|
||||
for stmt in &mut async_fn.block.stmts {
|
||||
if let syn::Stmt::Expr(syn::Expr::Match(syn::ExprMatch { expr: scrutinee, .. }), _) = stmt {
|
||||
@ -145,3 +143,36 @@ pub fn fake_desugar_await(_args: TokenStream, input: TokenStream) -> TokenStream
|
||||
|
||||
quote!(#async_fn).into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn rewrite_struct(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mut item_struct = parse_macro_input!(input as syn::ItemStruct);
|
||||
// remove struct attributes including doc comments.
|
||||
item_struct.attrs = vec![];
|
||||
if let Visibility::Public(token) = item_struct.vis {
|
||||
// set vis to `pub(crate)` to trigger `missing_docs_in_private_items` lint.
|
||||
let new_vis: Visibility = syn::parse_quote_spanned!(token.span() => pub(crate));
|
||||
item_struct.vis = new_vis;
|
||||
}
|
||||
if let syn::Fields::Named(fields) = &mut item_struct.fields {
|
||||
for field in &mut fields.named {
|
||||
// remove all attributes from fields as well.
|
||||
field.attrs = vec![];
|
||||
}
|
||||
}
|
||||
|
||||
quote!(#item_struct).into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn with_empty_docs(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let item = parse_macro_input!(input as syn::Item);
|
||||
let attrs: Vec<syn::Attribute> = vec![];
|
||||
let doc_comment = "";
|
||||
quote! {
|
||||
#(#attrs)*
|
||||
#[doc = #doc_comment]
|
||||
#item
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#![warn(clippy::await_holding_lock)]
|
||||
#![allow(clippy::readonly_write_lock)]
|
||||
|
||||
// When adding or modifying a test, please do the same for parking_lot::Mutex.
|
||||
mod std_mutex {
|
||||
|
@ -1,12 +1,12 @@
|
||||
error: this `MutexGuard` is held across an `await` point
|
||||
--> tests/ui/await_holding_lock.rs:9:13
|
||||
--> tests/ui/await_holding_lock.rs:10:13
|
||||
|
|
||||
LL | let guard = x.lock().unwrap();
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
|
||||
note: these are all the `await` points this lock is held through
|
||||
--> tests/ui/await_holding_lock.rs:11:15
|
||||
--> tests/ui/await_holding_lock.rs:12:15
|
||||
|
|
||||
LL | baz().await
|
||||
| ^^^^^
|
||||
@ -14,40 +14,40 @@ LL | baz().await
|
||||
= help: to override `-D warnings` add `#[allow(clippy::await_holding_lock)]`
|
||||
|
||||
error: this `MutexGuard` is held across an `await` point
|
||||
--> tests/ui/await_holding_lock.rs:25:13
|
||||
--> tests/ui/await_holding_lock.rs:26:13
|
||||
|
|
||||
LL | let guard = x.read().unwrap();
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
|
||||
note: these are all the `await` points this lock is held through
|
||||
--> tests/ui/await_holding_lock.rs:27:15
|
||||
--> tests/ui/await_holding_lock.rs:28:15
|
||||
|
|
||||
LL | baz().await
|
||||
| ^^^^^
|
||||
|
||||
error: this `MutexGuard` is held across an `await` point
|
||||
--> tests/ui/await_holding_lock.rs:31:13
|
||||
--> tests/ui/await_holding_lock.rs:32:13
|
||||
|
|
||||
LL | let mut guard = x.write().unwrap();
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
|
||||
note: these are all the `await` points this lock is held through
|
||||
--> tests/ui/await_holding_lock.rs:33:15
|
||||
--> tests/ui/await_holding_lock.rs:34:15
|
||||
|
|
||||
LL | baz().await
|
||||
| ^^^^^
|
||||
|
||||
error: this `MutexGuard` is held across an `await` point
|
||||
--> tests/ui/await_holding_lock.rs:53:13
|
||||
--> tests/ui/await_holding_lock.rs:54:13
|
||||
|
|
||||
LL | let guard = x.lock().unwrap();
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
|
||||
note: these are all the `await` points this lock is held through
|
||||
--> tests/ui/await_holding_lock.rs:56:28
|
||||
--> tests/ui/await_holding_lock.rs:57:28
|
||||
|
|
||||
LL | let second = baz().await;
|
||||
| ^^^^^
|
||||
@ -56,79 +56,79 @@ LL | let third = baz().await;
|
||||
| ^^^^^
|
||||
|
||||
error: this `MutexGuard` is held across an `await` point
|
||||
--> tests/ui/await_holding_lock.rs:67:17
|
||||
--> tests/ui/await_holding_lock.rs:68:17
|
||||
|
|
||||
LL | let guard = x.lock().unwrap();
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
|
||||
note: these are all the `await` points this lock is held through
|
||||
--> tests/ui/await_holding_lock.rs:69:19
|
||||
--> tests/ui/await_holding_lock.rs:70:19
|
||||
|
|
||||
LL | baz().await
|
||||
| ^^^^^
|
||||
|
||||
error: this `MutexGuard` is held across an `await` point
|
||||
--> tests/ui/await_holding_lock.rs:80:17
|
||||
--> tests/ui/await_holding_lock.rs:81:17
|
||||
|
|
||||
LL | let guard = x.lock().unwrap();
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
|
||||
note: these are all the `await` points this lock is held through
|
||||
--> tests/ui/await_holding_lock.rs:82:19
|
||||
--> tests/ui/await_holding_lock.rs:83:19
|
||||
|
|
||||
LL | baz().await
|
||||
| ^^^^^
|
||||
|
||||
error: this `MutexGuard` is held across an `await` point
|
||||
--> tests/ui/await_holding_lock.rs:93:13
|
||||
--> tests/ui/await_holding_lock.rs:94:13
|
||||
|
|
||||
LL | let guard = x.lock();
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
|
||||
note: these are all the `await` points this lock is held through
|
||||
--> tests/ui/await_holding_lock.rs:95:15
|
||||
--> tests/ui/await_holding_lock.rs:96:15
|
||||
|
|
||||
LL | baz().await
|
||||
| ^^^^^
|
||||
|
||||
error: this `MutexGuard` is held across an `await` point
|
||||
--> tests/ui/await_holding_lock.rs:109:13
|
||||
--> tests/ui/await_holding_lock.rs:110:13
|
||||
|
|
||||
LL | let guard = x.read();
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
|
||||
note: these are all the `await` points this lock is held through
|
||||
--> tests/ui/await_holding_lock.rs:111:15
|
||||
--> tests/ui/await_holding_lock.rs:112:15
|
||||
|
|
||||
LL | baz().await
|
||||
| ^^^^^
|
||||
|
||||
error: this `MutexGuard` is held across an `await` point
|
||||
--> tests/ui/await_holding_lock.rs:115:13
|
||||
--> tests/ui/await_holding_lock.rs:116:13
|
||||
|
|
||||
LL | let mut guard = x.write();
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
|
||||
note: these are all the `await` points this lock is held through
|
||||
--> tests/ui/await_holding_lock.rs:117:15
|
||||
--> tests/ui/await_holding_lock.rs:118:15
|
||||
|
|
||||
LL | baz().await
|
||||
| ^^^^^
|
||||
|
||||
error: this `MutexGuard` is held across an `await` point
|
||||
--> tests/ui/await_holding_lock.rs:137:13
|
||||
--> tests/ui/await_holding_lock.rs:138:13
|
||||
|
|
||||
LL | let guard = x.lock();
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
|
||||
note: these are all the `await` points this lock is held through
|
||||
--> tests/ui/await_holding_lock.rs:140:28
|
||||
--> tests/ui/await_holding_lock.rs:141:28
|
||||
|
|
||||
LL | let second = baz().await;
|
||||
| ^^^^^
|
||||
@ -137,40 +137,40 @@ LL | let third = baz().await;
|
||||
| ^^^^^
|
||||
|
||||
error: this `MutexGuard` is held across an `await` point
|
||||
--> tests/ui/await_holding_lock.rs:151:17
|
||||
--> tests/ui/await_holding_lock.rs:152:17
|
||||
|
|
||||
LL | let guard = x.lock();
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
|
||||
note: these are all the `await` points this lock is held through
|
||||
--> tests/ui/await_holding_lock.rs:153:19
|
||||
--> tests/ui/await_holding_lock.rs:154:19
|
||||
|
|
||||
LL | baz().await
|
||||
| ^^^^^
|
||||
|
||||
error: this `MutexGuard` is held across an `await` point
|
||||
--> tests/ui/await_holding_lock.rs:164:17
|
||||
--> tests/ui/await_holding_lock.rs:165:17
|
||||
|
|
||||
LL | let guard = x.lock();
|
||||
| ^^^^^
|
||||
|
|
||||
= help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
|
||||
note: these are all the `await` points this lock is held through
|
||||
--> tests/ui/await_holding_lock.rs:166:19
|
||||
--> tests/ui/await_holding_lock.rs:167:19
|
||||
|
|
||||
LL | baz().await
|
||||
| ^^^^^
|
||||
|
||||
error: this `MutexGuard` is held across an `await` point
|
||||
--> tests/ui/await_holding_lock.rs:185:9
|
||||
--> tests/ui/await_holding_lock.rs:186:9
|
||||
|
|
||||
LL | let mut guard = x.lock().unwrap();
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
|
||||
note: these are all the `await` points this lock is held through
|
||||
--> tests/ui/await_holding_lock.rs:189:11
|
||||
--> tests/ui/await_holding_lock.rs:190:11
|
||||
|
|
||||
LL | baz().await;
|
||||
| ^^^^^
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![allow(unused, clippy::assertions_on_constants)]
|
||||
#![allow(unused, clippy::assertions_on_constants, clippy::const_is_empty)]
|
||||
#![warn(clippy::bool_assert_comparison)]
|
||||
|
||||
use std::ops::Not;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![allow(unused, clippy::assertions_on_constants)]
|
||||
#![allow(unused, clippy::assertions_on_constants, clippy::const_is_empty)]
|
||||
#![warn(clippy::bool_assert_comparison)]
|
||||
|
||||
use std::ops::Not;
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![allow(dead_code)]
|
||||
#![warn(clippy::cast_lossless)]
|
||||
|
||||
type U8 = u8;
|
||||
|
||||
fn main() {
|
||||
// Test clippy::cast_lossless with casts to integer types
|
||||
let _ = u8::from(true);
|
||||
@ -19,6 +21,8 @@ fn main() {
|
||||
|
||||
// Test with an expression wrapped in parens
|
||||
let _ = u16::from(true | false);
|
||||
|
||||
let _ = U8::from(true);
|
||||
}
|
||||
|
||||
// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![allow(dead_code)]
|
||||
#![warn(clippy::cast_lossless)]
|
||||
|
||||
type U8 = u8;
|
||||
|
||||
fn main() {
|
||||
// Test clippy::cast_lossless with casts to integer types
|
||||
let _ = true as u8;
|
||||
@ -19,6 +21,8 @@ fn main() {
|
||||
|
||||
// Test with an expression wrapped in parens
|
||||
let _ = (true | false) as u16;
|
||||
|
||||
let _ = true as U8;
|
||||
}
|
||||
|
||||
// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:6:13
|
||||
--> tests/ui/cast_lossless_bool.rs:8:13
|
||||
|
|
||||
LL | let _ = true as u8;
|
||||
| ^^^^^^^^^^ help: try: `u8::from(true)`
|
||||
@ -8,82 +8,88 @@ LL | let _ = true as u8;
|
||||
= help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]`
|
||||
|
||||
error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:7:13
|
||||
--> tests/ui/cast_lossless_bool.rs:9:13
|
||||
|
|
||||
LL | let _ = true as u16;
|
||||
| ^^^^^^^^^^^ help: try: `u16::from(true)`
|
||||
|
||||
error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:8:13
|
||||
--> tests/ui/cast_lossless_bool.rs:10:13
|
||||
|
|
||||
LL | let _ = true as u32;
|
||||
| ^^^^^^^^^^^ help: try: `u32::from(true)`
|
||||
|
||||
error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:9:13
|
||||
--> tests/ui/cast_lossless_bool.rs:11:13
|
||||
|
|
||||
LL | let _ = true as u64;
|
||||
| ^^^^^^^^^^^ help: try: `u64::from(true)`
|
||||
|
||||
error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:10:13
|
||||
--> tests/ui/cast_lossless_bool.rs:12:13
|
||||
|
|
||||
LL | let _ = true as u128;
|
||||
| ^^^^^^^^^^^^ help: try: `u128::from(true)`
|
||||
|
||||
error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:11:13
|
||||
--> tests/ui/cast_lossless_bool.rs:13:13
|
||||
|
|
||||
LL | let _ = true as usize;
|
||||
| ^^^^^^^^^^^^^ help: try: `usize::from(true)`
|
||||
|
||||
error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:13:13
|
||||
--> tests/ui/cast_lossless_bool.rs:15:13
|
||||
|
|
||||
LL | let _ = true as i8;
|
||||
| ^^^^^^^^^^ help: try: `i8::from(true)`
|
||||
|
||||
error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:14:13
|
||||
--> tests/ui/cast_lossless_bool.rs:16:13
|
||||
|
|
||||
LL | let _ = true as i16;
|
||||
| ^^^^^^^^^^^ help: try: `i16::from(true)`
|
||||
|
||||
error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:15:13
|
||||
--> tests/ui/cast_lossless_bool.rs:17:13
|
||||
|
|
||||
LL | let _ = true as i32;
|
||||
| ^^^^^^^^^^^ help: try: `i32::from(true)`
|
||||
|
||||
error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:16:13
|
||||
--> tests/ui/cast_lossless_bool.rs:18:13
|
||||
|
|
||||
LL | let _ = true as i64;
|
||||
| ^^^^^^^^^^^ help: try: `i64::from(true)`
|
||||
|
||||
error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:17:13
|
||||
--> tests/ui/cast_lossless_bool.rs:19:13
|
||||
|
|
||||
LL | let _ = true as i128;
|
||||
| ^^^^^^^^^^^^ help: try: `i128::from(true)`
|
||||
|
||||
error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:18:13
|
||||
--> tests/ui/cast_lossless_bool.rs:20:13
|
||||
|
|
||||
LL | let _ = true as isize;
|
||||
| ^^^^^^^^^^^^^ help: try: `isize::from(true)`
|
||||
|
||||
error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:21:13
|
||||
--> tests/ui/cast_lossless_bool.rs:23:13
|
||||
|
|
||||
LL | let _ = (true | false) as u16;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)`
|
||||
|
||||
error: casting `bool` to `U8` is more cleanly stated with `U8::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:25:13
|
||||
|
|
||||
LL | let _ = true as U8;
|
||||
| ^^^^^^^^^^ help: try: `U8::from(true)`
|
||||
|
||||
error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
|
||||
--> tests/ui/cast_lossless_bool.rs:49:13
|
||||
--> tests/ui/cast_lossless_bool.rs:53:13
|
||||
|
|
||||
LL | let _ = true as u8;
|
||||
| ^^^^^^^^^^ help: try: `u8::from(true)`
|
||||
|
||||
error: aborting due to 14 previous errors
|
||||
error: aborting due to 15 previous errors
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
|
||||
#![warn(clippy::cast_lossless)]
|
||||
|
||||
type F32 = f32;
|
||||
type F64 = f64;
|
||||
|
||||
fn main() {
|
||||
// Test clippy::cast_lossless with casts to floating-point types
|
||||
let x0 = 1i8;
|
||||
let _ = f32::from(x0);
|
||||
let _ = f64::from(x0);
|
||||
let _ = F32::from(x0);
|
||||
let _ = F64::from(x0);
|
||||
let x1 = 1u8;
|
||||
let _ = f32::from(x1);
|
||||
let _ = f64::from(x1);
|
||||
|
@ -1,11 +1,16 @@
|
||||
#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
|
||||
#![warn(clippy::cast_lossless)]
|
||||
|
||||
type F32 = f32;
|
||||
type F64 = f64;
|
||||
|
||||
fn main() {
|
||||
// Test clippy::cast_lossless with casts to floating-point types
|
||||
let x0 = 1i8;
|
||||
let _ = x0 as f32;
|
||||
let _ = x0 as f64;
|
||||
let _ = x0 as F32;
|
||||
let _ = x0 as F64;
|
||||
let x1 = 1u8;
|
||||
let _ = x1 as f32;
|
||||
let _ = x1 as f64;
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: casting `i8` to `f32` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_float.rs:7:13
|
||||
--> tests/ui/cast_lossless_float.rs:10:13
|
||||
|
|
||||
LL | let _ = x0 as f32;
|
||||
| ^^^^^^^^^ help: try: `f32::from(x0)`
|
||||
@ -8,64 +8,76 @@ LL | let _ = x0 as f32;
|
||||
= help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]`
|
||||
|
||||
error: casting `i8` to `f64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_float.rs:8:13
|
||||
--> tests/ui/cast_lossless_float.rs:11:13
|
||||
|
|
||||
LL | let _ = x0 as f64;
|
||||
| ^^^^^^^^^ help: try: `f64::from(x0)`
|
||||
|
||||
error: casting `i8` to `F32` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_float.rs:12:13
|
||||
|
|
||||
LL | let _ = x0 as F32;
|
||||
| ^^^^^^^^^ help: try: `F32::from(x0)`
|
||||
|
||||
error: casting `i8` to `F64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_float.rs:13:13
|
||||
|
|
||||
LL | let _ = x0 as F64;
|
||||
| ^^^^^^^^^ help: try: `F64::from(x0)`
|
||||
|
||||
error: casting `u8` to `f32` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_float.rs:10:13
|
||||
--> tests/ui/cast_lossless_float.rs:15:13
|
||||
|
|
||||
LL | let _ = x1 as f32;
|
||||
| ^^^^^^^^^ help: try: `f32::from(x1)`
|
||||
|
||||
error: casting `u8` to `f64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_float.rs:11:13
|
||||
--> tests/ui/cast_lossless_float.rs:16:13
|
||||
|
|
||||
LL | let _ = x1 as f64;
|
||||
| ^^^^^^^^^ help: try: `f64::from(x1)`
|
||||
|
||||
error: casting `i16` to `f32` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_float.rs:13:13
|
||||
--> tests/ui/cast_lossless_float.rs:18:13
|
||||
|
|
||||
LL | let _ = x2 as f32;
|
||||
| ^^^^^^^^^ help: try: `f32::from(x2)`
|
||||
|
||||
error: casting `i16` to `f64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_float.rs:14:13
|
||||
--> tests/ui/cast_lossless_float.rs:19:13
|
||||
|
|
||||
LL | let _ = x2 as f64;
|
||||
| ^^^^^^^^^ help: try: `f64::from(x2)`
|
||||
|
||||
error: casting `u16` to `f32` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_float.rs:16:13
|
||||
--> tests/ui/cast_lossless_float.rs:21:13
|
||||
|
|
||||
LL | let _ = x3 as f32;
|
||||
| ^^^^^^^^^ help: try: `f32::from(x3)`
|
||||
|
||||
error: casting `u16` to `f64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_float.rs:17:13
|
||||
--> tests/ui/cast_lossless_float.rs:22:13
|
||||
|
|
||||
LL | let _ = x3 as f64;
|
||||
| ^^^^^^^^^ help: try: `f64::from(x3)`
|
||||
|
||||
error: casting `i32` to `f64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_float.rs:19:13
|
||||
--> tests/ui/cast_lossless_float.rs:24:13
|
||||
|
|
||||
LL | let _ = x4 as f64;
|
||||
| ^^^^^^^^^ help: try: `f64::from(x4)`
|
||||
|
||||
error: casting `u32` to `f64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_float.rs:21:13
|
||||
--> tests/ui/cast_lossless_float.rs:26:13
|
||||
|
|
||||
LL | let _ = x5 as f64;
|
||||
| ^^^^^^^^^ help: try: `f64::from(x5)`
|
||||
|
||||
error: casting `f32` to `f64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_float.rs:24:13
|
||||
--> tests/ui/cast_lossless_float.rs:29:13
|
||||
|
|
||||
LL | let _ = 1.0f32 as f64;
|
||||
| ^^^^^^^^^^^^^ help: try: `f64::from(1.0f32)`
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
|
||||
#![warn(clippy::cast_lossless)]
|
||||
|
||||
type I64 = i64;
|
||||
type U128 = u128;
|
||||
|
||||
fn main() {
|
||||
// Test clippy::cast_lossless with casts to integer types
|
||||
let _ = i16::from(1i8);
|
||||
@ -24,6 +27,13 @@ fn main() {
|
||||
|
||||
// Test with an expression wrapped in parens
|
||||
let _ = u16::from(1u8 + 1u8);
|
||||
|
||||
let _ = I64::from(1i8);
|
||||
|
||||
// Do not lint if destination type is u128
|
||||
// see https://github.com/rust-lang/rust-clippy/issues/12492
|
||||
let _ = 1u8 as u128;
|
||||
let _ = 1u8 as U128;
|
||||
}
|
||||
|
||||
// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
|
||||
|
@ -1,6 +1,9 @@
|
||||
#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
|
||||
#![warn(clippy::cast_lossless)]
|
||||
|
||||
type I64 = i64;
|
||||
type U128 = u128;
|
||||
|
||||
fn main() {
|
||||
// Test clippy::cast_lossless with casts to integer types
|
||||
let _ = 1i8 as i16;
|
||||
@ -24,6 +27,13 @@ fn main() {
|
||||
|
||||
// Test with an expression wrapped in parens
|
||||
let _ = (1u8 + 1u8) as u16;
|
||||
|
||||
let _ = 1i8 as I64;
|
||||
|
||||
// Do not lint if destination type is u128
|
||||
// see https://github.com/rust-lang/rust-clippy/issues/12492
|
||||
let _ = 1u8 as u128;
|
||||
let _ = 1u8 as U128;
|
||||
}
|
||||
|
||||
// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: casting `i8` to `i16` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:6:13
|
||||
--> tests/ui/cast_lossless_integer.rs:9:13
|
||||
|
|
||||
LL | let _ = 1i8 as i16;
|
||||
| ^^^^^^^^^^ help: try: `i16::from(1i8)`
|
||||
@ -8,124 +8,130 @@ LL | let _ = 1i8 as i16;
|
||||
= help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]`
|
||||
|
||||
error: casting `i8` to `i32` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:7:13
|
||||
--> tests/ui/cast_lossless_integer.rs:10:13
|
||||
|
|
||||
LL | let _ = 1i8 as i32;
|
||||
| ^^^^^^^^^^ help: try: `i32::from(1i8)`
|
||||
|
||||
error: casting `i8` to `i64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:8:13
|
||||
--> tests/ui/cast_lossless_integer.rs:11:13
|
||||
|
|
||||
LL | let _ = 1i8 as i64;
|
||||
| ^^^^^^^^^^ help: try: `i64::from(1i8)`
|
||||
|
||||
error: casting `u8` to `i16` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:9:13
|
||||
--> tests/ui/cast_lossless_integer.rs:12:13
|
||||
|
|
||||
LL | let _ = 1u8 as i16;
|
||||
| ^^^^^^^^^^ help: try: `i16::from(1u8)`
|
||||
|
||||
error: casting `u8` to `i32` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:10:13
|
||||
--> tests/ui/cast_lossless_integer.rs:13:13
|
||||
|
|
||||
LL | let _ = 1u8 as i32;
|
||||
| ^^^^^^^^^^ help: try: `i32::from(1u8)`
|
||||
|
||||
error: casting `u8` to `i64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:11:13
|
||||
--> tests/ui/cast_lossless_integer.rs:14:13
|
||||
|
|
||||
LL | let _ = 1u8 as i64;
|
||||
| ^^^^^^^^^^ help: try: `i64::from(1u8)`
|
||||
|
||||
error: casting `u8` to `u16` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:12:13
|
||||
--> tests/ui/cast_lossless_integer.rs:15:13
|
||||
|
|
||||
LL | let _ = 1u8 as u16;
|
||||
| ^^^^^^^^^^ help: try: `u16::from(1u8)`
|
||||
|
||||
error: casting `u8` to `u32` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:13:13
|
||||
--> tests/ui/cast_lossless_integer.rs:16:13
|
||||
|
|
||||
LL | let _ = 1u8 as u32;
|
||||
| ^^^^^^^^^^ help: try: `u32::from(1u8)`
|
||||
|
||||
error: casting `u8` to `u64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:14:13
|
||||
--> tests/ui/cast_lossless_integer.rs:17:13
|
||||
|
|
||||
LL | let _ = 1u8 as u64;
|
||||
| ^^^^^^^^^^ help: try: `u64::from(1u8)`
|
||||
|
||||
error: casting `i16` to `i32` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:15:13
|
||||
--> tests/ui/cast_lossless_integer.rs:18:13
|
||||
|
|
||||
LL | let _ = 1i16 as i32;
|
||||
| ^^^^^^^^^^^ help: try: `i32::from(1i16)`
|
||||
|
||||
error: casting `i16` to `i64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:16:13
|
||||
--> tests/ui/cast_lossless_integer.rs:19:13
|
||||
|
|
||||
LL | let _ = 1i16 as i64;
|
||||
| ^^^^^^^^^^^ help: try: `i64::from(1i16)`
|
||||
|
||||
error: casting `u16` to `i32` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:17:13
|
||||
--> tests/ui/cast_lossless_integer.rs:20:13
|
||||
|
|
||||
LL | let _ = 1u16 as i32;
|
||||
| ^^^^^^^^^^^ help: try: `i32::from(1u16)`
|
||||
|
||||
error: casting `u16` to `i64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:18:13
|
||||
--> tests/ui/cast_lossless_integer.rs:21:13
|
||||
|
|
||||
LL | let _ = 1u16 as i64;
|
||||
| ^^^^^^^^^^^ help: try: `i64::from(1u16)`
|
||||
|
||||
error: casting `u16` to `u32` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:19:13
|
||||
--> tests/ui/cast_lossless_integer.rs:22:13
|
||||
|
|
||||
LL | let _ = 1u16 as u32;
|
||||
| ^^^^^^^^^^^ help: try: `u32::from(1u16)`
|
||||
|
||||
error: casting `u16` to `u64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:20:13
|
||||
--> tests/ui/cast_lossless_integer.rs:23:13
|
||||
|
|
||||
LL | let _ = 1u16 as u64;
|
||||
| ^^^^^^^^^^^ help: try: `u64::from(1u16)`
|
||||
|
||||
error: casting `i32` to `i64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:21:13
|
||||
--> tests/ui/cast_lossless_integer.rs:24:13
|
||||
|
|
||||
LL | let _ = 1i32 as i64;
|
||||
| ^^^^^^^^^^^ help: try: `i64::from(1i32)`
|
||||
|
||||
error: casting `u32` to `i64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:22:13
|
||||
--> tests/ui/cast_lossless_integer.rs:25:13
|
||||
|
|
||||
LL | let _ = 1u32 as i64;
|
||||
| ^^^^^^^^^^^ help: try: `i64::from(1u32)`
|
||||
|
||||
error: casting `u32` to `u64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:23:13
|
||||
--> tests/ui/cast_lossless_integer.rs:26:13
|
||||
|
|
||||
LL | let _ = 1u32 as u64;
|
||||
| ^^^^^^^^^^^ help: try: `u64::from(1u32)`
|
||||
|
||||
error: casting `u8` to `u16` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:26:13
|
||||
--> tests/ui/cast_lossless_integer.rs:29:13
|
||||
|
|
||||
LL | let _ = (1u8 + 1u8) as u16;
|
||||
| ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)`
|
||||
|
||||
error: casting `i8` to `I64` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:31:13
|
||||
|
|
||||
LL | let _ = 1i8 as I64;
|
||||
| ^^^^^^^^^^ help: try: `I64::from(1i8)`
|
||||
|
||||
error: casting `i8` to `i32` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:60:13
|
||||
--> tests/ui/cast_lossless_integer.rs:70:13
|
||||
|
|
||||
LL | let _ = sign_cast!(x, u8, i8) as i32;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8))`
|
||||
|
||||
error: casting `i8` to `i32` may become silently lossy if you later change the type
|
||||
--> tests/ui/cast_lossless_integer.rs:61:13
|
||||
--> tests/ui/cast_lossless_integer.rs:71:13
|
||||
|
|
||||
LL | let _ = (sign_cast!(x, u8, i8) + 1) as i32;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8) + 1)`
|
||||
|
||||
error: aborting due to 21 previous errors
|
||||
error: aborting due to 22 previous errors
|
||||
|
||||
|
174
tests/ui/const_is_empty.rs
Normal file
174
tests/ui/const_is_empty.rs
Normal file
@ -0,0 +1,174 @@
|
||||
#![feature(inline_const)]
|
||||
#![warn(clippy::const_is_empty)]
|
||||
#![allow(clippy::needless_late_init, unused_must_use)]
|
||||
|
||||
fn test_literal() {
|
||||
if "".is_empty() {
|
||||
//~^ERROR: this expression always evaluates to true
|
||||
}
|
||||
if "foobar".is_empty() {
|
||||
//~^ERROR: this expression always evaluates to false
|
||||
}
|
||||
}
|
||||
|
||||
fn test_byte_literal() {
|
||||
if b"".is_empty() {
|
||||
//~^ERROR: this expression always evaluates to true
|
||||
}
|
||||
if b"foobar".is_empty() {
|
||||
//~^ERROR: this expression always evaluates to false
|
||||
}
|
||||
}
|
||||
|
||||
fn test_no_mut() {
|
||||
let mut empty = "";
|
||||
if empty.is_empty() {
|
||||
// No lint because it is mutable
|
||||
}
|
||||
}
|
||||
|
||||
fn test_propagated() {
|
||||
let empty = "";
|
||||
let non_empty = "foobar";
|
||||
let empty2 = empty;
|
||||
let non_empty2 = non_empty;
|
||||
if empty2.is_empty() {
|
||||
//~^ERROR: this expression always evaluates to true
|
||||
}
|
||||
if non_empty2.is_empty() {
|
||||
//~^ERROR: this expression always evaluates to false
|
||||
}
|
||||
}
|
||||
|
||||
const EMPTY_STR: &str = "";
|
||||
const NON_EMPTY_STR: &str = "foo";
|
||||
const EMPTY_BSTR: &[u8] = b"";
|
||||
const NON_EMPTY_BSTR: &[u8] = b"foo";
|
||||
const EMPTY_U8_SLICE: &[u8] = &[];
|
||||
const NON_EMPTY_U8_SLICE: &[u8] = &[1, 2];
|
||||
const EMPTY_SLICE: &[u32] = &[];
|
||||
const NON_EMPTY_SLICE: &[u32] = &[1, 2];
|
||||
const NON_EMPTY_SLICE_REPEAT: &[u32] = &[1; 2];
|
||||
const EMPTY_ARRAY: [u32; 0] = [];
|
||||
const EMPTY_ARRAY_REPEAT: [u32; 0] = [1; 0];
|
||||
const NON_EMPTY_ARRAY: [u32; 2] = [1, 2];
|
||||
const NON_EMPTY_ARRAY_REPEAT: [u32; 2] = [1; 2];
|
||||
const EMPTY_REF_ARRAY: &[u32; 0] = &[];
|
||||
const NON_EMPTY_REF_ARRAY: &[u32; 3] = &[1, 2, 3];
|
||||
|
||||
fn test_from_const() {
|
||||
let _ = EMPTY_STR.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to true
|
||||
let _ = NON_EMPTY_STR.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to false
|
||||
let _ = EMPTY_BSTR.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to true
|
||||
let _ = NON_EMPTY_BSTR.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to false
|
||||
let _ = EMPTY_ARRAY.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to true
|
||||
let _ = EMPTY_ARRAY_REPEAT.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to true
|
||||
let _ = EMPTY_U8_SLICE.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to true
|
||||
let _ = NON_EMPTY_U8_SLICE.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to false
|
||||
let _ = NON_EMPTY_ARRAY.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to false
|
||||
let _ = NON_EMPTY_ARRAY_REPEAT.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to false
|
||||
let _ = EMPTY_REF_ARRAY.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to true
|
||||
let _ = NON_EMPTY_REF_ARRAY.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to false
|
||||
let _ = EMPTY_SLICE.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to true
|
||||
let _ = NON_EMPTY_SLICE.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to false
|
||||
let _ = NON_EMPTY_SLICE_REPEAT.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to false
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let value = "foobar";
|
||||
let _ = value.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to false
|
||||
let x = value;
|
||||
let _ = x.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to false
|
||||
let _ = "".is_empty();
|
||||
//~^ ERROR: this expression always evaluates to true
|
||||
let _ = b"".is_empty();
|
||||
//~^ ERROR: this expression always evaluates to true
|
||||
}
|
||||
|
||||
fn str_from_arg(var: &str) {
|
||||
var.is_empty();
|
||||
// Do not lint, we know nothiny about var
|
||||
}
|
||||
|
||||
fn update_str() {
|
||||
let mut value = "duck";
|
||||
value = "penguin";
|
||||
|
||||
let _ = value.is_empty();
|
||||
// Do not lint since value is mutable
|
||||
}
|
||||
|
||||
fn macros() {
|
||||
// Content from Macro
|
||||
let file = include_str!("const_is_empty.rs");
|
||||
let _ = file.is_empty();
|
||||
// No lint because initializer comes from a macro result
|
||||
|
||||
let var = env!("PATH");
|
||||
let _ = var.is_empty();
|
||||
// No lint because initializer comes from a macro result
|
||||
}
|
||||
|
||||
fn conditional_value() {
|
||||
let value;
|
||||
|
||||
if true {
|
||||
value = "hey";
|
||||
} else {
|
||||
value = "hej";
|
||||
}
|
||||
|
||||
let _ = value.is_empty();
|
||||
// Do not lint, current constant folding is too simple to detect this
|
||||
}
|
||||
|
||||
fn cfg_conditioned() {
|
||||
#[cfg(test)]
|
||||
let val = "";
|
||||
#[cfg(not(test))]
|
||||
let val = "foo";
|
||||
|
||||
let _ = val.is_empty();
|
||||
// Do not lint, value depend on a #[cfg(…)] directive
|
||||
}
|
||||
|
||||
fn not_cfg_conditioned() {
|
||||
let val = "";
|
||||
#[cfg(not(target_os = "inexistent"))]
|
||||
let _ = val.is_empty();
|
||||
//~^ ERROR: this expression always evaluates to true
|
||||
}
|
||||
|
||||
const fn const_rand() -> &'static str {
|
||||
"17"
|
||||
}
|
||||
|
||||
fn const_expressions() {
|
||||
let _ = const { if true { "1" } else { "2" } }.is_empty();
|
||||
// Do not lint, we do not recurse into boolean expressions
|
||||
|
||||
let _ = const_rand().is_empty();
|
||||
// Do not lint, we do not recurse into functions
|
||||
}
|
||||
|
||||
fn constant_from_external_crate() {
|
||||
let _ = std::env::consts::EXE_EXTENSION.is_empty();
|
||||
// Do not lint, `exe_ext` comes from the `std` crate
|
||||
}
|
161
tests/ui/const_is_empty.stderr
Normal file
161
tests/ui/const_is_empty.stderr
Normal file
@ -0,0 +1,161 @@
|
||||
error: this expression always evaluates to true
|
||||
--> tests/ui/const_is_empty.rs:6:8
|
||||
|
|
||||
LL | if "".is_empty() {
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::const-is-empty` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::const_is_empty)]`
|
||||
|
||||
error: this expression always evaluates to false
|
||||
--> tests/ui/const_is_empty.rs:9:8
|
||||
|
|
||||
LL | if "foobar".is_empty() {
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to true
|
||||
--> tests/ui/const_is_empty.rs:15:8
|
||||
|
|
||||
LL | if b"".is_empty() {
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to false
|
||||
--> tests/ui/const_is_empty.rs:18:8
|
||||
|
|
||||
LL | if b"foobar".is_empty() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to true
|
||||
--> tests/ui/const_is_empty.rs:35:8
|
||||
|
|
||||
LL | if empty2.is_empty() {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to false
|
||||
--> tests/ui/const_is_empty.rs:38:8
|
||||
|
|
||||
LL | if non_empty2.is_empty() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to true
|
||||
--> tests/ui/const_is_empty.rs:60:13
|
||||
|
|
||||
LL | let _ = EMPTY_STR.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to false
|
||||
--> tests/ui/const_is_empty.rs:62:13
|
||||
|
|
||||
LL | let _ = NON_EMPTY_STR.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to true
|
||||
--> tests/ui/const_is_empty.rs:64:13
|
||||
|
|
||||
LL | let _ = EMPTY_BSTR.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to false
|
||||
--> tests/ui/const_is_empty.rs:66:13
|
||||
|
|
||||
LL | let _ = NON_EMPTY_BSTR.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to true
|
||||
--> tests/ui/const_is_empty.rs:68:13
|
||||
|
|
||||
LL | let _ = EMPTY_ARRAY.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to true
|
||||
--> tests/ui/const_is_empty.rs:70:13
|
||||
|
|
||||
LL | let _ = EMPTY_ARRAY_REPEAT.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to true
|
||||
--> tests/ui/const_is_empty.rs:72:13
|
||||
|
|
||||
LL | let _ = EMPTY_U8_SLICE.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to false
|
||||
--> tests/ui/const_is_empty.rs:74:13
|
||||
|
|
||||
LL | let _ = NON_EMPTY_U8_SLICE.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to false
|
||||
--> tests/ui/const_is_empty.rs:76:13
|
||||
|
|
||||
LL | let _ = NON_EMPTY_ARRAY.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to false
|
||||
--> tests/ui/const_is_empty.rs:78:13
|
||||
|
|
||||
LL | let _ = NON_EMPTY_ARRAY_REPEAT.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to true
|
||||
--> tests/ui/const_is_empty.rs:80:13
|
||||
|
|
||||
LL | let _ = EMPTY_REF_ARRAY.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to false
|
||||
--> tests/ui/const_is_empty.rs:82:13
|
||||
|
|
||||
LL | let _ = NON_EMPTY_REF_ARRAY.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to true
|
||||
--> tests/ui/const_is_empty.rs:84:13
|
||||
|
|
||||
LL | let _ = EMPTY_SLICE.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to false
|
||||
--> tests/ui/const_is_empty.rs:86:13
|
||||
|
|
||||
LL | let _ = NON_EMPTY_SLICE.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to false
|
||||
--> tests/ui/const_is_empty.rs:88:13
|
||||
|
|
||||
LL | let _ = NON_EMPTY_SLICE_REPEAT.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to false
|
||||
--> tests/ui/const_is_empty.rs:94:13
|
||||
|
|
||||
LL | let _ = value.is_empty();
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to false
|
||||
--> tests/ui/const_is_empty.rs:97:13
|
||||
|
|
||||
LL | let _ = x.is_empty();
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to true
|
||||
--> tests/ui/const_is_empty.rs:99:13
|
||||
|
|
||||
LL | let _ = "".is_empty();
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to true
|
||||
--> tests/ui/const_is_empty.rs:101:13
|
||||
|
|
||||
LL | let _ = b"".is_empty();
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: this expression always evaluates to true
|
||||
--> tests/ui/const_is_empty.rs:155:13
|
||||
|
|
||||
LL | let _ = val.is_empty();
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 26 previous errors
|
||||
|
7
tests/ui/crashes/ice-12491.fixed
Normal file
7
tests/ui/crashes/ice-12491.fixed
Normal file
@ -0,0 +1,7 @@
|
||||
#![warn(clippy::needless_return)]
|
||||
|
||||
fn main() {
|
||||
if (true) {
|
||||
// anything一些中文
|
||||
}
|
||||
}
|
8
tests/ui/crashes/ice-12491.rs
Normal file
8
tests/ui/crashes/ice-12491.rs
Normal file
@ -0,0 +1,8 @@
|
||||
#![warn(clippy::needless_return)]
|
||||
|
||||
fn main() {
|
||||
if (true) {
|
||||
// anything一些中文
|
||||
return;
|
||||
}
|
||||
}
|
19
tests/ui/crashes/ice-12491.stderr
Normal file
19
tests/ui/crashes/ice-12491.stderr
Normal file
@ -0,0 +1,19 @@
|
||||
error: unneeded `return` statement
|
||||
--> tests/ui/crashes/ice-12491.rs:5:24
|
||||
|
|
||||
LL | // anything一些中文
|
||||
| ____________________________^
|
||||
LL | | return;
|
||||
| |______________^
|
||||
|
|
||||
= note: `-D clippy::needless-return` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::needless_return)]`
|
||||
help: remove `return`
|
||||
|
|
||||
LL - // anything一些中文
|
||||
LL - return;
|
||||
LL + // anything一些中文
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
111
tests/ui/dbg_macro/dbg_macro.fixed
Normal file
111
tests/ui/dbg_macro/dbg_macro.fixed
Normal file
@ -0,0 +1,111 @@
|
||||
#![warn(clippy::dbg_macro)]
|
||||
#![allow(clippy::unnecessary_operation, clippy::no_effect)]
|
||||
|
||||
fn foo(n: u32) -> u32 {
|
||||
if let Some(n) = n.checked_sub(4) { n } else { n }
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
}
|
||||
fn bar(_: ()) {}
|
||||
|
||||
fn factorial(n: u32) -> u32 {
|
||||
if n <= 1 {
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
1
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
} else {
|
||||
n * factorial(n - 1)
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
42;
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
foo(3) + factorial(4);
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
(1, 2, 3, 4, 5);
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
}
|
||||
|
||||
fn issue9914() {
|
||||
macro_rules! foo {
|
||||
($x:expr) => {
|
||||
$x;
|
||||
};
|
||||
}
|
||||
macro_rules! foo2 {
|
||||
($x:expr) => {
|
||||
$x;
|
||||
};
|
||||
}
|
||||
macro_rules! expand_to_dbg {
|
||||
() => {
|
||||
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
#[allow(clippy::let_unit_value)]
|
||||
let _ = ();
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
bar(());
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
foo!(());
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
foo2!(foo!(()));
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
expand_to_dbg!();
|
||||
}
|
||||
|
||||
mod issue7274 {
|
||||
trait Thing<'b> {
|
||||
fn foo(&self);
|
||||
}
|
||||
|
||||
macro_rules! define_thing {
|
||||
($thing:ident, $body:expr) => {
|
||||
impl<'a> Thing<'a> for $thing {
|
||||
fn foo<'b>(&self) {
|
||||
$body
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct MyThing;
|
||||
define_thing!(MyThing, {
|
||||
2;
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn issue8481() {
|
||||
1;
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn foo2() {
|
||||
1;
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod mod1 {
|
||||
fn func() {
|
||||
1;
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
}
|
||||
}
|
||||
|
||||
mod issue12131 {
|
||||
fn dbg_in_print(s: &str) {
|
||||
println!("dbg: {:?}", s);
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
print!("{}", s);
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
}
|
||||
}
|
@ -1,9 +1,5 @@
|
||||
//@no-rustfix
|
||||
|
||||
#![warn(clippy::dbg_macro)]
|
||||
|
||||
#[path = "auxiliary/submodule.rs"]
|
||||
mod submodule;
|
||||
#![allow(clippy::unnecessary_operation, clippy::no_effect)]
|
||||
|
||||
fn foo(n: u32) -> u32 {
|
||||
if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
|
||||
@ -25,12 +21,8 @@ fn factorial(n: u32) -> u32 {
|
||||
fn main() {
|
||||
dbg!(42);
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
dbg!(dbg!(dbg!(42)));
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
foo(3) + dbg!(factorial(4));
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
dbg!(1, 2, dbg!(3, 4));
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
dbg!(1, 2, 3, 4, 5);
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
}
|
||||
@ -49,6 +41,7 @@ fn issue9914() {
|
||||
macro_rules! expand_to_dbg {
|
||||
() => {
|
||||
dbg!();
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
};
|
||||
}
|
||||
|
||||
@ -107,3 +100,12 @@ mod mod1 {
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
}
|
||||
}
|
||||
|
||||
mod issue12131 {
|
||||
fn dbg_in_print(s: &str) {
|
||||
println!("dbg: {:?}", dbg!(s));
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
print!("{}", dbg!(s));
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +1,18 @@
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/auxiliary/submodule.rs:2:5
|
||||
|
|
||||
LL | dbg!();
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::dbg-macro` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]`
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL - dbg!();
|
||||
LL +
|
||||
|
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:9:22
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:5:22
|
||||
|
|
||||
LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::dbg-macro` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]`
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL | if let Some(n) = n.checked_sub(4) { n } else { n }
|
||||
| ~~~~~~~~~~~~~~~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:15:8
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:11:8
|
||||
|
|
||||
LL | if dbg!(n <= 1) {
|
||||
| ^^^^^^^^^^^^
|
||||
@ -35,7 +23,7 @@ LL | if n <= 1 {
|
||||
| ~~~~~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:17:9
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:13:9
|
||||
|
|
||||
LL | dbg!(1)
|
||||
| ^^^^^^^
|
||||
@ -46,7 +34,7 @@ LL | 1
|
||||
|
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:20:9
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:16:9
|
||||
|
|
||||
LL | dbg!(n * factorial(n - 1))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -57,7 +45,7 @@ LL | n * factorial(n - 1)
|
||||
|
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:26:5
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:22:5
|
||||
|
|
||||
LL | dbg!(42);
|
||||
| ^^^^^^^^
|
||||
@ -68,18 +56,7 @@ LL | 42;
|
||||
| ~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:28:5
|
||||
|
|
||||
LL | dbg!(dbg!(dbg!(42)));
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL | dbg!(dbg!(42));
|
||||
| ~~~~~~~~~~~~~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:30:14
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:24:14
|
||||
|
|
||||
LL | foo(3) + dbg!(factorial(4));
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
@ -90,18 +67,7 @@ LL | foo(3) + factorial(4);
|
||||
| ~~~~~~~~~~~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:32:5
|
||||
|
|
||||
LL | dbg!(1, 2, dbg!(3, 4));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL | (1, 2, dbg!(3, 4));
|
||||
| ~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:34:5
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:26:5
|
||||
|
|
||||
LL | dbg!(1, 2, 3, 4, 5);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
@ -112,7 +78,7 @@ LL | (1, 2, 3, 4, 5);
|
||||
| ~~~~~~~~~~~~~~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:55:5
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:48:5
|
||||
|
|
||||
LL | dbg!();
|
||||
| ^^^^^^^
|
||||
@ -124,7 +90,7 @@ LL +
|
||||
|
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:58:13
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:51:13
|
||||
|
|
||||
LL | let _ = dbg!();
|
||||
| ^^^^^^
|
||||
@ -135,7 +101,7 @@ LL | let _ = ();
|
||||
| ~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:60:9
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:53:9
|
||||
|
|
||||
LL | bar(dbg!());
|
||||
| ^^^^^^
|
||||
@ -146,7 +112,7 @@ LL | bar(());
|
||||
| ~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:62:10
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:55:10
|
||||
|
|
||||
LL | foo!(dbg!());
|
||||
| ^^^^^^
|
||||
@ -157,7 +123,7 @@ LL | foo!(());
|
||||
| ~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:64:16
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:57:16
|
||||
|
|
||||
LL | foo2!(foo!(dbg!()));
|
||||
| ^^^^^^
|
||||
@ -168,7 +134,23 @@ LL | foo2!(foo!(()));
|
||||
| ~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:86:9
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:43:13
|
||||
|
|
||||
LL | dbg!();
|
||||
| ^^^^^^^
|
||||
...
|
||||
LL | expand_to_dbg!();
|
||||
| ---------------- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `expand_to_dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL - dbg!();
|
||||
LL +
|
||||
|
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:79:9
|
||||
|
|
||||
LL | dbg!(2);
|
||||
| ^^^^^^^
|
||||
@ -179,7 +161,7 @@ LL | 2;
|
||||
| ~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:93:5
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:86:5
|
||||
|
|
||||
LL | dbg!(1);
|
||||
| ^^^^^^^
|
||||
@ -190,7 +172,7 @@ LL | 1;
|
||||
| ~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:99:5
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:92:5
|
||||
|
|
||||
LL | dbg!(1);
|
||||
| ^^^^^^^
|
||||
@ -201,7 +183,7 @@ LL | 1;
|
||||
| ~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:106:9
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:99:9
|
||||
|
|
||||
LL | dbg!(1);
|
||||
| ^^^^^^^
|
||||
@ -211,5 +193,27 @@ help: remove the invocation before committing it to a version control system
|
||||
LL | 1;
|
||||
| ~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:106:31
|
||||
|
|
||||
LL | println!("dbg: {:?}", dbg!(s));
|
||||
| ^^^^^^^
|
||||
|
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL | println!("dbg: {:?}", s);
|
||||
| ~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro.rs:108:22
|
||||
|
|
||||
LL | print!("{}", dbg!(s));
|
||||
| ^^^^^^^
|
||||
|
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL | print!("{}", s);
|
||||
| ~
|
||||
|
||||
error: aborting due to 19 previous errors
|
||||
|
||||
|
12
tests/ui/dbg_macro/dbg_macro_unfixable.rs
Normal file
12
tests/ui/dbg_macro/dbg_macro_unfixable.rs
Normal file
@ -0,0 +1,12 @@
|
||||
//@no-rustfix
|
||||
#![warn(clippy::dbg_macro)]
|
||||
|
||||
#[path = "auxiliary/submodule.rs"]
|
||||
mod submodule;
|
||||
|
||||
fn main() {
|
||||
dbg!(dbg!(dbg!(42)));
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
dbg!(1, 2, dbg!(3, 4));
|
||||
//~^ ERROR: the `dbg!` macro is intended as a debugging tool
|
||||
}
|
71
tests/ui/dbg_macro/dbg_macro_unfixable.stderr
Normal file
71
tests/ui/dbg_macro/dbg_macro_unfixable.stderr
Normal file
@ -0,0 +1,71 @@
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/auxiliary/submodule.rs:2:5
|
||||
|
|
||||
LL | dbg!();
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::dbg-macro` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]`
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL - dbg!();
|
||||
LL +
|
||||
|
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro_unfixable.rs:8:5
|
||||
|
|
||||
LL | dbg!(dbg!(dbg!(42)));
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL | dbg!(dbg!(42));
|
||||
| ~~~~~~~~~~~~~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro_unfixable.rs:8:10
|
||||
|
|
||||
LL | dbg!(dbg!(dbg!(42)));
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL | dbg!(dbg!(42));
|
||||
| ~~~~~~~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro_unfixable.rs:8:15
|
||||
|
|
||||
LL | dbg!(dbg!(dbg!(42)));
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL | dbg!(dbg!(42));
|
||||
| ~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro_unfixable.rs:10:5
|
||||
|
|
||||
LL | dbg!(1, 2, dbg!(3, 4));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL | (1, 2, dbg!(3, 4));
|
||||
| ~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: the `dbg!` macro is intended as a debugging tool
|
||||
--> tests/ui/dbg_macro/dbg_macro_unfixable.rs:10:16
|
||||
|
|
||||
LL | dbg!(1, 2, dbg!(3, 4));
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
help: remove the invocation before committing it to a version control system
|
||||
|
|
||||
LL | dbg!(1, 2, (3, 4));
|
||||
| ~~~~~~
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user