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

This commit is contained in:
Philipp Krones 2024-03-07 17:05:53 +01:00
commit a6df0277ea
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
168 changed files with 4414 additions and 2678 deletions

4
.github/driver.sh vendored
View file

@ -50,11 +50,11 @@ diff -u normalized.stderr tests/ui/double_neg.stderr
# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same
SYSROOT=$(rustc --print sysroot)
diff -u <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose)
diff -u <(./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose)
echo "fn main() {}" >target/driver_test.rs
# we can't run 2 rustcs on the same file at the same time
CLIPPY=$(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc)
CLIPPY=$(./target/debug/clippy-driver ./target/driver_test.rs --rustc)
RUSTC=$(rustc ./target/driver_test.rs)
diff -u <($CLIPPY) <($RUSTC)

View file

@ -44,11 +44,6 @@ jobs:
run: rustup show active-toolchain
# Run
- name: Set LD_LIBRARY_PATH (Linux)
run: |
SYSROOT=$(rustc --print sysroot)
echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
- name: Build
run: cargo build --tests --features deny-warnings,internal
@ -72,6 +67,6 @@ jobs:
working-directory: clippy_dev
- name: Test clippy-driver
run: bash .github/driver.sh
env:
OS: ${{ runner.os }}
run: |
TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ')
rustup run $TOOLCHAIN bash .github/driver.sh

View file

@ -59,7 +59,7 @@ jobs:
host: i686-unknown-linux-gnu
- os: windows-latest
host: x86_64-pc-windows-msvc
- os: macos-latest
- os: macos-13
host: x86_64-apple-darwin
runs-on: ${{ matrix.os }}
@ -87,23 +87,6 @@ jobs:
rustup show active-toolchain
# Run
- name: Set LD_LIBRARY_PATH (Linux)
if: runner.os == 'Linux'
run: |
SYSROOT=$(rustc --print sysroot)
echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
- name: Link rustc dylib (MacOS)
if: runner.os == 'macOS'
run: |
SYSROOT=$(rustc --print sysroot)
sudo mkdir -p /usr/local/lib
sudo find "${SYSROOT}/lib" -maxdepth 1 -name '*dylib' -exec ln -s {} /usr/local/lib \;
- name: Set PATH (Windows)
if: runner.os == 'Windows'
run: |
SYSROOT=$(rustc --print sysroot)
echo "$SYSROOT/bin" >> $GITHUB_PATH
- name: Build
run: cargo build --tests --features deny-warnings,internal
@ -136,7 +119,9 @@ jobs:
working-directory: clippy_dev
- name: Test clippy-driver
run: bash .github/driver.sh
run: |
TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ')
rustup run $TOOLCHAIN bash .github/driver.sh
env:
OS: ${{ runner.os }}
@ -236,11 +221,6 @@ jobs:
- name: Install toolchain
run: rustup show active-toolchain
- name: Set LD_LIBRARY_PATH
run: |
SYSROOT=$(rustc --print sysroot)
echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
# Download
- name: Download target dir
uses: actions/download-artifact@v3
@ -254,8 +234,8 @@ jobs:
# Run
- name: Test ${{ matrix.integration }}
run: |
RUSTUP_TOOLCHAIN="$(rustup show active-toolchain | grep -o -E "nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}")" \
$CARGO_TARGET_DIR/debug/integration --show-output
TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ')
rustup run $TOOLCHAIN $CARGO_TARGET_DIR/debug/integration --show-output
env:
INTEGRATION: ${{ matrix.integration }}

View file

@ -5046,6 +5046,7 @@ Released 2018-09-13
[`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states
[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
[`assigning_clones`]: https://rust-lang.github.io/rust-clippy/master/index.html#assigning_clones
[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type
[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
@ -5423,6 +5424,7 @@ Released 2018-09-13
[`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop
[`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods
[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
[`mixed_attributes_style`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_attributes_style
[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
[`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression
[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
@ -5816,74 +5818,74 @@ Released 2018-09-13
[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
<!-- end autogenerated links to lint list -->
<!-- begin autogenerated links to configuration documentation -->
[`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates
[`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments
[`accept-comment-above-attributes`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-attributes
[`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement
[`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero
[`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests
[`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests
[`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args
[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
[`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests
[`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception
[`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
[`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates
[`allowed-idents-below-min-chars`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-idents-below-min-chars
[`allowed-scripts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-scripts
[`allowed-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-wildcard-imports
[`arithmetic-side-effects-allowed`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed
[`arithmetic-side-effects-allowed-binary`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed-binary
[`arithmetic-side-effects-allowed-unary`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed-unary
[`avoid-breaking-exported-api`]: https://doc.rust-lang.org/clippy/lint_configuration.html#avoid-breaking-exported-api
[`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv
[`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold
[`excessive-nesting-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#excessive-nesting-threshold
[`disallowed-names`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-names
[`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline
[`semicolon-outside-block-ignore-multiline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-outside-block-ignore-multiline
[`doc-valid-idents`]: https://doc.rust-lang.org/clippy/lint_configuration.html#doc-valid-idents
[`too-many-arguments-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-arguments-threshold
[`type-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#type-complexity-threshold
[`single-char-binding-names-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#single-char-binding-names-threshold
[`too-large-for-stack`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-large-for-stack
[`enum-variant-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-name-threshold
[`struct-field-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#struct-field-name-threshold
[`enum-variant-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-size-threshold
[`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold
[`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold
[`trivial-copy-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trivial-copy-size-limit
[`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit
[`too-many-lines-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-lines-threshold
[`array-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#array-size-threshold
[`stack-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#stack-size-threshold
[`vec-box-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#vec-box-size-threshold
[`max-trait-bounds`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-trait-bounds
[`max-struct-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-struct-bools
[`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools
[`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports
[`avoid-breaking-exported-api`]: https://doc.rust-lang.org/clippy/lint_configuration.html#avoid-breaking-exported-api
[`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types
[`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish
[`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
[`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold
[`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros
[`disallowed-methods`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-methods
[`disallowed-names`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-names
[`disallowed-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-types
[`doc-valid-idents`]: https://doc.rust-lang.org/clippy/lint_configuration.html#doc-valid-idents
[`enable-raw-pointer-heuristic-for-send`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enable-raw-pointer-heuristic-for-send
[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow
[`enforced-import-renames`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforced-import-renames
[`enum-variant-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-name-threshold
[`enum-variant-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-size-threshold
[`excessive-nesting-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#excessive-nesting-threshold
[`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold
[`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability
[`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold
[`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold
[`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else
[`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools
[`max-include-file-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-include-file-size
[`max-struct-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-struct-bools
[`max-suggested-slice-pattern-length`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-suggested-slice-pattern-length
[`max-trait-bounds`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-trait-bounds
[`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold
[`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items
[`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv
[`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit
[`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior
[`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline
[`semicolon-outside-block-ignore-multiline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-outside-block-ignore-multiline
[`single-char-binding-names-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#single-char-binding-names-threshold
[`stack-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#stack-size-threshold
[`standard-macro-braces`]: https://doc.rust-lang.org/clippy/lint_configuration.html#standard-macro-braces
[`struct-field-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#struct-field-name-threshold
[`suppress-restriction-lint-in-const`]: https://doc.rust-lang.org/clippy/lint_configuration.html#suppress-restriction-lint-in-const
[`too-large-for-stack`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-large-for-stack
[`too-many-arguments-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-arguments-threshold
[`too-many-lines-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-lines-threshold
[`trivial-copy-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trivial-copy-size-limit
[`type-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#type-complexity-threshold
[`unnecessary-box-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#unnecessary-box-size
[`unreadable-literal-lint-fractions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#unreadable-literal-lint-fractions
[`upper-case-acronyms-aggressive`]: https://doc.rust-lang.org/clippy/lint_configuration.html#upper-case-acronyms-aggressive
[`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else
[`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish
[`standard-macro-braces`]: https://doc.rust-lang.org/clippy/lint_configuration.html#standard-macro-braces
[`enforced-import-renames`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforced-import-renames
[`allowed-scripts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-scripts
[`enable-raw-pointer-heuristic-for-send`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enable-raw-pointer-heuristic-for-send
[`max-suggested-slice-pattern-length`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-suggested-slice-pattern-length
[`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types
[`max-include-file-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-include-file-size
[`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests
[`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests
[`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests
[`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests
[`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold
[`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability
[`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args
[`suppress-restriction-lint-in-const`]: https://doc.rust-lang.org/clippy/lint_configuration.html#suppress-restriction-lint-in-const
[`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items
[`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold
[`unnecessary-box-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#unnecessary-box-size
[`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception
[`allowed-idents-below-min-chars`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-idents-below-min-chars
[`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold
[`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement
[`accept-comment-above-attributes`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-attributes
[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
[`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments
[`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
[`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates
[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow
[`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
[`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior
[`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero
[`allowed-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-wildcard-imports
[`vec-box-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#vec-box-size-threshold
[`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold
[`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports
<!-- end autogenerated links to configuration documentation -->

View file

@ -94,6 +94,53 @@ impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait {
}
```
## Creating Types Programmatically
Traits are often generic over a type parameter, e.g. `Borrow<T>` is generic
over `T`. Rust allows us to implement a trait for a specific type. For example,
we can implement `Borrow<[u8]>` for a hypothetical type `Foo`. Let's suppose
that we would like to find whether our type actually implements `Borrow<[u8]>`.
To do so, we can use the same `implements_trait` function as above, and supply
a type parameter that represents `[u8]`. Since `[u8]` is a specialization of
`[T]`, we can use the [`Ty::new_slice`][new_slice] method to create a type
that represents `[T]` and supply `u8` as a type parameter.
To create a `ty::Ty` programmatically, we rely on `Ty::new_*` methods. These
methods create a `TyKind` and then wrap it in a `Ty` struct. This means we
have access to all the primitive types, such as `Ty::new_char`,
`Ty::new_bool`, `Ty::new_int`, etc. We can also create more complex types,
such as slices, tuples, and references out of these basic building blocks.
For trait checking, it is not enough to create the types, we need to convert
them into [GenericArg]. In rustc, a generic is an entity that the compiler
understands and has three kinds, type, const and lifetime. By calling
`.into()` on a constructed [Ty], we wrap the type into a generic which can
then be used by the query system to decide whether the specialized trait
is implemented.
The following code demonstrates how to do this:
```rust
use rustc_middle::ty::Ty;
use clippy_utils::ty::implements_trait;
use rustc_span::symbol::sym;
let ty = todo!("Get the `Foo` type to check for a trait implementation");
let borrow_id = cx.tcx.get_diagnostic_item(sym::Borrow).unwrap(); // avoid unwrap in real code
let slice_of_bytes_t = Ty::new_slice(cx.tcx, cx.tcx.types.u8);
let generic_param = slice_of_bytes_t.into();
if implements_trait(cx, ty, borrow_id, &[generic_param]) {
todo!("Rest of lint implementation")
}
```
In essence, the [Ty] struct allows us to create types programmatically in a
representation that can be used by the compiler and the query engine. We then
use the `rustc_middle::Ty` of the type we are interested in, and query the
compiler to see if it indeed implements the trait we are interested in.
[DefId]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html
[diagnostic_items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
[lang_items]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/lang_items/struct.LanguageItems.html
@ -102,4 +149,7 @@ impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait {
[symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html
[symbol_index]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_span/symbol/sym/index.html
[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html
[rust]: https://github.com/rust-lang/rust
[new_slice]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.new_slice
[GenericArg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.GenericArg.html

View file

@ -123,6 +123,22 @@ the [`TypeckResults::node_type()`][node_type] method inside of bodies.
> **Warning**: Don't use `hir_ty_to_ty` inside of bodies, because this can cause ICEs.
## Creating Types programmatically
A common usecase for creating types programmatically is when we want to check if a type implements a trait (see
[Trait Checking](trait_checking.md)).
Here's an example of how to create a `Ty` for a slice of `u8`, i.e. `[u8]`
```rust
use rustc_middle::ty::Ty;
// assume we have access to a LateContext
let ty = Ty::new_slice(cx.tcx, Ty::new_u8());
```
In general, we rely on `Ty::new_*` methods. These methods define the basic building-blocks that the
type-system and trait-system use to define and understand the written code.
## Useful Links
Below are some useful links to further explore the concepts covered

File diff suppressed because it is too large Load diff

View file

@ -193,7 +193,7 @@ macro_rules! define_Conf {
}
pub fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
vec![
let mut sorted = vec![
$(
{
let deprecation_reason = wrap_option!($($dep)?);
@ -206,7 +206,9 @@ macro_rules! define_Conf {
)
},
)+
]
];
sorted.sort_by(|a, b| a.name.cmp(&b.name));
sorted
}
};
}

View file

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

View file

@ -26,9 +26,11 @@ impl ClippyConfiguration {
doc_comment: &'static str,
deprecation_reason: Option<&'static str>,
) -> Self {
let (lints, doc) = parse_config_field_doc(doc_comment)
let (mut lints, doc) = parse_config_field_doc(doc_comment)
.unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
lints.sort();
Self {
name: to_kebab(name),
lints,

View file

@ -24,6 +24,7 @@ msrv_aliases! {
1,68,0 { PATH_MAIN_SEPARATOR_STR }
1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
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 }
1,55,0 { SEEK_REWIND }
1,54,0 { INTO_KEYS }

View file

@ -0,0 +1,322 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::HirNode;
use clippy_utils::sugg::Sugg;
use clippy_utils::{is_trait_method, path_to_local};
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_span::def_id::DefId;
use rustc_span::symbol::sym;
use rustc_span::ExpnKind;
declare_clippy_lint! {
/// ### What it does
/// Checks for code like `foo = bar.clone();`
///
/// ### Why is this bad?
/// Custom `Clone::clone_from()` or `ToOwned::clone_into` implementations allow the objects
/// to share resources and therefore avoid allocations.
///
/// ### Example
/// ```rust
/// struct Thing;
///
/// impl Clone for Thing {
/// fn clone(&self) -> Self { todo!() }
/// fn clone_from(&mut self, other: &Self) { todo!() }
/// }
///
/// pub fn assign_to_ref(a: &mut Thing, b: Thing) {
/// *a = b.clone();
/// }
/// ```
/// Use instead:
/// ```rust
/// struct Thing;
/// impl Clone for Thing {
/// fn clone(&self) -> Self { todo!() }
/// fn clone_from(&mut self, other: &Self) { todo!() }
/// }
///
/// pub fn assign_to_ref(a: &mut Thing, b: Thing) {
/// a.clone_from(&b);
/// }
/// ```
#[clippy::version = "1.77.0"]
pub ASSIGNING_CLONES,
perf,
"assigning the result of cloning may be inefficient"
}
declare_lint_pass!(AssigningClones => [ASSIGNING_CLONES]);
impl<'tcx> LateLintPass<'tcx> for AssigningClones {
fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx hir::Expr<'_>) {
// Do not fire the lint in macros
let expn_data = assign_expr.span().ctxt().outer_expn_data();
match expn_data.kind {
ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) | ExpnKind::Macro(..) => return,
ExpnKind::Root => {},
}
let ExprKind::Assign(lhs, rhs, _span) = assign_expr.kind else {
return;
};
let Some(call) = extract_call(cx, rhs) else {
return;
};
if is_ok_to_suggest(cx, lhs, &call) {
suggest(cx, assign_expr, lhs, &call);
}
}
}
// Try to resolve the call to `Clone::clone` or `ToOwned::to_owned`.
fn extract_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<CallCandidate<'tcx>> {
let fn_def_id = clippy_utils::fn_def_id(cx, expr)?;
// Fast paths to only check method calls without arguments or function calls with a single argument
let (target, kind, resolved_method) = match expr.kind {
ExprKind::MethodCall(path, receiver, [], _span) => {
let args = cx.typeck_results().node_args(expr.hir_id);
// If we could not resolve the method, don't apply the lint
let Ok(Some(resolved_method)) = Instance::resolve(cx.tcx, cx.param_env, fn_def_id, args) else {
return None;
};
if is_trait_method(cx, expr, sym::Clone) && path.ident.name == sym::clone {
(TargetTrait::Clone, CallKind::MethodCall { receiver }, resolved_method)
} else if is_trait_method(cx, expr, sym::ToOwned) && path.ident.name.as_str() == "to_owned" {
(TargetTrait::ToOwned, CallKind::MethodCall { receiver }, resolved_method)
} else {
return None;
}
},
ExprKind::Call(function, [arg]) => {
let kind = cx.typeck_results().node_type(function.hir_id).kind();
// If we could not resolve the method, don't apply the lint
let Ok(Some(resolved_method)) = (match kind {
ty::FnDef(_, args) => Instance::resolve(cx.tcx, cx.param_env, fn_def_id, args),
_ => Ok(None),
}) else {
return None;
};
if cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id) {
(
TargetTrait::ToOwned,
CallKind::FunctionCall { self_arg: arg },
resolved_method,
)
} else if let Some(trait_did) = cx.tcx.trait_of_item(fn_def_id)
&& cx.tcx.is_diagnostic_item(sym::Clone, trait_did)
{
(
TargetTrait::Clone,
CallKind::FunctionCall { self_arg: arg },
resolved_method,
)
} else {
return None;
}
},
_ => return None,
};
Some(CallCandidate {
target,
kind,
method_def_id: resolved_method.def_id(),
})
}
// 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 {
// 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) {
// TODO: This check currently bails if the local variable has no initializer.
// That is overly conservative - the lint should fire even if there was no initializer,
// but the variable has been initialized before `lhs` was evaluated.
if let Some(Node::Local(local)) = cx.tcx.hir().parent_id_iter(local).next().map(|p| cx.tcx.hir_node(p))
&& local.init.is_none()
{
return false;
}
}
let Some(impl_block) = cx.tcx.impl_of_method(call.method_def_id) else {
return false;
};
// If the method implementation comes from #[derive(Clone)], then don't suggest the lint.
// Automatically generated Clone impls do not currently override `clone_from`.
// See e.g. https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305 for more details.
if cx.tcx.is_builtin_derived(impl_block) {
return false;
}
// Find the function for which we want to check that it is implemented.
let provided_fn = match call.target {
TargetTrait::Clone => cx.tcx.get_diagnostic_item(sym::Clone).and_then(|clone| {
cx.tcx
.provided_trait_methods(clone)
.find(|item| item.name == sym::clone_from)
}),
TargetTrait::ToOwned => cx.tcx.get_diagnostic_item(sym::ToOwned).and_then(|to_owned| {
cx.tcx
.provided_trait_methods(to_owned)
.find(|item| item.name.as_str() == "clone_into")
}),
};
let Some(provided_fn) = provided_fn else {
return false;
};
// Now take a look if the impl block defines an implementation for the method that we're interested
// in. If not, then we're using a default implementation, which is not interesting, so we will
// not suggest the lint.
let implemented_fns = cx.tcx.impl_item_implementor_ids(impl_block);
implemented_fns.contains_key(&provided_fn.def_id)
}
fn suggest<'tcx>(
cx: &LateContext<'tcx>,
assign_expr: &hir::Expr<'tcx>,
lhs: &hir::Expr<'tcx>,
call: &CallCandidate<'tcx>,
) {
span_lint_and_then(cx, ASSIGNING_CLONES, assign_expr.span, call.message(), |diag| {
let mut applicability = Applicability::MachineApplicable;
diag.span_suggestion(
assign_expr.span,
call.suggestion_msg(),
call.suggested_replacement(cx, lhs, &mut applicability),
applicability,
);
});
}
#[derive(Copy, Clone, Debug)]
enum CallKind<'tcx> {
MethodCall { receiver: &'tcx Expr<'tcx> },
FunctionCall { self_arg: &'tcx Expr<'tcx> },
}
#[derive(Copy, Clone, Debug)]
enum TargetTrait {
Clone,
ToOwned,
}
#[derive(Debug)]
struct CallCandidate<'tcx> {
target: TargetTrait,
kind: CallKind<'tcx>,
// DefId of the called method from an impl block that implements the target trait
method_def_id: DefId,
}
impl<'tcx> CallCandidate<'tcx> {
#[inline]
fn message(&self) -> &'static str {
match self.target {
TargetTrait::Clone => "assigning the result of `Clone::clone()` may be inefficient",
TargetTrait::ToOwned => "assigning the result of `ToOwned::to_owned()` may be inefficient",
}
}
#[inline]
fn suggestion_msg(&self) -> &'static str {
match self.target {
TargetTrait::Clone => "use `clone_from()`",
TargetTrait::ToOwned => "use `clone_into()`",
}
}
fn suggested_replacement(
&self,
cx: &LateContext<'tcx>,
lhs: &hir::Expr<'tcx>,
applicability: &mut Applicability,
) -> String {
match self.target {
TargetTrait::Clone => {
match self.kind {
CallKind::MethodCall { receiver } => {
let receiver_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
// `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
Sugg::hir_with_applicability(cx, ref_expr, "_", applicability)
} else {
// `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
Sugg::hir_with_applicability(cx, lhs, "_", applicability)
}
.maybe_par();
// Determine whether we need to reference the argument to clone_from().
let clone_receiver_type = cx.typeck_results().expr_ty(receiver);
let clone_receiver_adj_type = cx.typeck_results().expr_ty_adjusted(receiver);
let mut arg_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability);
if clone_receiver_type != clone_receiver_adj_type {
// The receiver may have been a value type, so we need to add an `&` to
// be sure the argument to clone_from will be a reference.
arg_sugg = arg_sugg.addr();
};
format!("{receiver_sugg}.clone_from({arg_sugg})")
},
CallKind::FunctionCall { self_arg, .. } => {
let self_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
// `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)`
Sugg::hir_with_applicability(cx, ref_expr, "_", applicability)
} else {
// `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)`
Sugg::hir_with_applicability(cx, lhs, "_", applicability).mut_addr()
};
// The RHS had to be exactly correct before the call, there is no auto-deref for function calls.
let rhs_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability);
format!("Clone::clone_from({self_sugg}, {rhs_sugg})")
},
}
},
TargetTrait::ToOwned => {
let rhs_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
// `*lhs = rhs.to_owned()` -> `rhs.clone_into(lhs)`
// `*lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, lhs)`
let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", applicability).maybe_par();
let inner_type = cx.typeck_results().expr_ty(ref_expr);
// If after unwrapping the dereference, the type is not a mutable reference, we add &mut to make it
// deref to a mutable reference.
if matches!(inner_type.kind(), ty::Ref(_, _, Mutability::Mut)) {
sugg
} else {
sugg.mut_addr()
}
} else {
// `lhs = rhs.to_owned()` -> `rhs.clone_into(&mut lhs)`
// `lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, &mut lhs)`
Sugg::hir_with_applicability(cx, lhs, "_", applicability)
.maybe_par()
.mut_addr()
};
match self.kind {
CallKind::MethodCall { receiver } => {
let receiver_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability);
format!("{receiver_sugg}.clone_into({rhs_sugg})")
},
CallKind::FunctionCall { self_arg, .. } => {
let self_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability);
format!("ToOwned::clone_into({self_sugg}, {rhs_sugg})")
},
}
},
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
use super::{Attribute, ALLOW_ATTRIBUTES_WITHOUT_REASON};
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_from_proc_macro;
use rustc_ast::{MetaItemKind, NestedMetaItem};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_span::sym;
use rustc_span::symbol::Symbol;
pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMetaItem], attr: &'cx Attribute) {
// Check for the feature
if !cx.tcx.features().lint_reasons {
return;
}
// Check if the reason is present
if let Some(item) = items.last().and_then(NestedMetaItem::meta_item)
&& let MetaItemKind::NameValue(_) = &item.kind
&& item.path == sym::reason
{
return;
}
// Check if the attribute is in an external macro and therefore out of the developer's control
if in_external_macro(cx.sess(), attr.span) || is_from_proc_macro(cx, &attr) {
return;
}
span_lint_and_help(
cx,
ALLOW_ATTRIBUTES_WITHOUT_REASON,
attr.span,
&format!("`{}` attribute without specifying a reason", name.as_str()),
None,
"try adding a reason at the end with `, reason = \"..\"`",
);
}

View file

@ -0,0 +1,44 @@
use super::utils::extract_clippy_lint;
use super::BLANKET_CLIPPY_RESTRICTION_LINTS;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use rustc_ast::NestedMetaItem;
use rustc_lint::{LateContext, Level, LintContext};
use rustc_span::symbol::Symbol;
use rustc_span::{sym, DUMMY_SP};
pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) {
for lint in items {
if let Some(lint_name) = extract_clippy_lint(lint) {
if lint_name.as_str() == "restriction" && name != sym::allow {
span_lint_and_help(
cx,
BLANKET_CLIPPY_RESTRICTION_LINTS,
lint.span(),
"`clippy::restriction` is not meant to be enabled as a group",
None,
"enable the restriction lints you need individually",
);
}
}
}
}
pub(super) fn check_command_line(cx: &LateContext<'_>) {
for (name, level) in &cx.sess().opts.lint_opts {
if name == "clippy::restriction" && *level > Level::Allow {
span_lint_and_then(
cx,
BLANKET_CLIPPY_RESTRICTION_LINTS,
DUMMY_SP,
"`clippy::restriction` is not meant to be enabled as a group",
|diag| {
diag.note(format!(
"because of the command line `--{} clippy::restriction`",
level.as_str()
));
diag.help("enable the restriction lints you need individually");
},
);
}
}
}

View file

@ -0,0 +1,87 @@
use super::{unnecessary_clippy_cfg, Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR};
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_ast::AttrStyle;
use rustc_errors::Applicability;
use rustc_lint::EarlyContext;
use rustc_span::sym;
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) {
// check cfg_attr
if attr.has_name(sym::cfg_attr)
&& let Some(items) = attr.meta_item_list()
&& items.len() == 2
&& let Some(feature_item) = items[0].meta_item()
{
// check for `rustfmt`
if feature_item.has_name(sym::rustfmt)
&& msrv.meets(msrvs::TOOL_ATTRIBUTES)
// check for `rustfmt_skip` and `rustfmt::skip`
&& let Some(skip_item) = &items[1].meta_item()
&& (skip_item.has_name(sym!(rustfmt_skip))
|| skip_item
.path
.segments
.last()
.expect("empty path in attribute")
.ident
.name
== sym::skip)
// Only lint outer attributes, because custom inner attributes are unstable
// Tracking issue: https://github.com/rust-lang/rust/issues/54726
&& attr.style == AttrStyle::Outer
{
span_lint_and_sugg(
cx,
DEPRECATED_CFG_ATTR,
attr.span,
"`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes",
"use",
"#[rustfmt::skip]".to_string(),
Applicability::MachineApplicable,
);
} else {
check_deprecated_cfg_recursively(cx, feature_item);
if let Some(behind_cfg_attr) = items[1].meta_item() {
unnecessary_clippy_cfg::check(cx, feature_item, behind_cfg_attr, attr);
}
}
}
}
pub(super) fn check_clippy(cx: &EarlyContext<'_>, attr: &Attribute) {
if attr.has_name(sym::cfg)
&& let Some(list) = attr.meta_item_list()
{
for item in list.iter().filter_map(|item| item.meta_item()) {
check_deprecated_cfg_recursively(cx, item);
}
}
}
fn check_deprecated_cfg_recursively(cx: &EarlyContext<'_>, attr: &rustc_ast::MetaItem) {
if let Some(ident) = attr.ident() {
if ["any", "all", "not"].contains(&ident.name.as_str()) {
let Some(list) = attr.meta_item_list() else { return };
for item in list.iter().filter_map(|item| item.meta_item()) {
check_deprecated_cfg_recursively(cx, item);
}
} else {
check_cargo_clippy_attr(cx, attr);
}
}
}
fn check_cargo_clippy_attr(cx: &EarlyContext<'_>, item: &rustc_ast::MetaItem) {
if item.has_name(sym::feature) && item.value_str().is_some_and(|v| v.as_str() == "cargo-clippy") {
span_lint_and_sugg(
cx,
DEPRECATED_CLIPPY_CFG_ATTR,
item.span,
"`feature = \"cargo-clippy\"` was replaced by `clippy`",
"replace with",
"clippy".to_string(),
Applicability::MachineApplicable,
);
}
}

View file

@ -0,0 +1,20 @@
use super::DEPRECATED_SEMVER;
use clippy_utils::diagnostics::span_lint;
use rustc_ast::{LitKind, MetaItemLit};
use rustc_lint::LateContext;
use rustc_span::Span;
use semver::Version;
pub(super) fn check(cx: &LateContext<'_>, span: Span, lit: &MetaItemLit) {
if let LitKind::Str(is, _) = lit.kind {
if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() {
return;
}
}
span_lint(
cx,
DEPRECATED_SEMVER,
span,
"the since field must contain a semver-compliant version",
);
}

View file

@ -0,0 +1,52 @@
use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::source::{is_present_in_source, snippet_opt, without_block_comments};
use rustc_ast::{AttrKind, AttrStyle};
use rustc_lint::EarlyContext;
use rustc_span::Span;
/// Check for empty lines after outer attributes.
///
/// Attributes and documentation comments are both considered outer attributes
/// by the AST. However, the average user likely considers them to be different.
/// Checking for empty lines after each of these attributes is split into two different
/// lints but can share the same logic.
pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
let mut iter = item.attrs.iter().peekable();
while let Some(attr) = iter.next() {
if (matches!(attr.kind, AttrKind::Normal(..)) || matches!(attr.kind, AttrKind::DocComment(..)))
&& attr.style == AttrStyle::Outer
&& is_present_in_source(cx, attr.span)
{
let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent());
let end_of_attr_to_next_attr_or_item = Span::new(
attr.span.hi(),
iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()),
item.span.ctxt(),
item.span.parent(),
);
if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) {
let lines = snippet.split('\n').collect::<Vec<_>>();
let lines = without_block_comments(lines);
if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
let (lint_msg, lint_type) = match attr.kind {
AttrKind::DocComment(..) => (
"found an empty line after a doc comment. \
Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?",
EMPTY_LINE_AFTER_DOC_COMMENTS,
),
AttrKind::Normal(..) => (
"found an empty line after an outer attribute. \
Perhaps you forgot to add a `!` to make it an inner attribute?",
EMPTY_LINE_AFTER_OUTER_ATTR,
),
};
span_lint(cx, lint_type, begin_of_attr_to_item, lint_msg);
}
}
}
}
}

View file

@ -0,0 +1,29 @@
use super::utils::is_word;
use super::INLINE_ALWAYS;
use clippy_utils::diagnostics::span_lint;
use rustc_ast::Attribute;
use rustc_lint::LateContext;
use rustc_span::symbol::Symbol;
use rustc_span::{sym, Span};
pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
if span.from_expansion() {
return;
}
for attr in attrs {
if let Some(values) = attr.meta_item_list() {
if values.len() != 1 || !attr.has_name(sym::inline) {
continue;
}
if is_word(&values[0], sym::always) {
span_lint(
cx,
INLINE_ALWAYS,
attr.span,
&format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
);
}
}
}
}

View file

@ -0,0 +1,51 @@
use super::{Attribute, MAYBE_MISUSED_CFG};
use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_ast::{MetaItemKind, NestedMetaItem};
use rustc_errors::Applicability;
use rustc_lint::EarlyContext;
use rustc_span::sym;
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
if attr.has_name(sym::cfg)
&& let Some(items) = attr.meta_item_list()
{
check_nested_misused_cfg(cx, &items);
}
}
fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
for item in items {
if let NestedMetaItem::MetaItem(meta) = item {
if let Some(ident) = meta.ident()
&& ident.name.as_str() == "features"
&& let Some(val) = meta.value_str()
{
span_lint_and_sugg(
cx,
MAYBE_MISUSED_CFG,
meta.span,
"'feature' may be misspelled as 'features'",
"did you mean",
format!("feature = \"{val}\""),
Applicability::MaybeIncorrect,
);
}
if let MetaItemKind::List(list) = &meta.kind {
check_nested_misused_cfg(cx, list);
// If this is not a list, then we check for `cfg(test)`.
} else if let Some(ident) = meta.ident()
&& matches!(ident.name.as_str(), "tests" | "Test")
{
span_lint_and_sugg(
cx,
MAYBE_MISUSED_CFG,
meta.span,
&format!("'test' may be misspelled as '{}'", ident.name.as_str()),
"did you mean",
"test".to_string(),
Applicability::MaybeIncorrect,
);
}
}
}
}

View file

@ -0,0 +1,90 @@
use super::{Attribute, MISMATCHED_TARGET_OS};
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_ast::{MetaItemKind, NestedMetaItem};
use rustc_errors::Applicability;
use rustc_lint::EarlyContext;
use rustc_span::{sym, Span};
static UNIX_SYSTEMS: &[&str] = &[
"android",
"dragonfly",
"emscripten",
"freebsd",
"fuchsia",
"haiku",
"illumos",
"ios",
"l4re",
"linux",
"macos",
"netbsd",
"openbsd",
"redox",
"solaris",
"vxworks",
];
// NOTE: windows is excluded from the list because it's also a valid target family.
static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"];
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
fn find_os(name: &str) -> Option<&'static str> {
UNIX_SYSTEMS
.iter()
.chain(NON_UNIX_SYSTEMS.iter())
.find(|&&os| os == name)
.copied()
}
fn is_unix(name: &str) -> bool {
UNIX_SYSTEMS.iter().any(|&os| os == name)
}
fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
let mut mismatched = Vec::new();
for item in items {
if let NestedMetaItem::MetaItem(meta) = item {
match &meta.kind {
MetaItemKind::List(list) => {
mismatched.extend(find_mismatched_target_os(list));
},
MetaItemKind::Word => {
if let Some(ident) = meta.ident()
&& let Some(os) = find_os(ident.name.as_str())
{
mismatched.push((os, ident.span));
}
},
MetaItemKind::NameValue(..) => {},
}
}
}
mismatched
}
if attr.has_name(sym::cfg)
&& let Some(list) = attr.meta_item_list()
&& let mismatched = find_mismatched_target_os(&list)
&& !mismatched.is_empty()
{
let mess = "operating system used in target family position";
span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| {
// Avoid showing the unix suggestion multiple times in case
// we have more than one mismatch for unix-like systems
let mut unix_suggested = false;
for (os, span) in mismatched {
let sugg = format!("target_os = \"{os}\"");
diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect);
if !unix_suggested && is_unix(os) {
diag.help("did you mean `unix`?");
unix_suggested = true;
}
}
});
}
}

View file

@ -0,0 +1,30 @@
use super::MIXED_ATTRIBUTES_STYLE;
use clippy_utils::diagnostics::span_lint;
use rustc_ast::AttrStyle;
use rustc_lint::EarlyContext;
pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
let mut has_outer = false;
let mut has_inner = false;
for attr in &item.attrs {
if attr.span.from_expansion() {
continue;
}
match attr.style {
AttrStyle::Inner => has_inner = true,
AttrStyle::Outer => has_outer = true,
}
}
if !has_outer || !has_inner {
return;
}
let mut attrs_iter = item.attrs.iter().filter(|attr| !attr.span.from_expansion());
let span = attrs_iter.next().unwrap().span;
span_lint(
cx,
MIXED_ATTRIBUTES_STYLE,
span.with_hi(attrs_iter.last().unwrap().span.hi()),
"item has both inner and outer attributes",
);
}

View file

@ -0,0 +1,588 @@
//! checks for attributes
mod allow_attributes_without_reason;
mod blanket_clippy_restriction_lints;
mod deprecated_cfg_attr;
mod deprecated_semver;
mod empty_line_after;
mod inline_always;
mod maybe_misused_cfg;
mod mismatched_target_os;
mod mixed_attributes_style;
mod non_minimal_cfg;
mod should_panic_without_expect;
mod unnecessary_clippy_cfg;
mod useless_attribute;
mod utils;
use clippy_config::msrvs::Msrv;
use rustc_ast::{Attribute, 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};
use rustc_span::sym;
use utils::{is_lint_level, is_relevant_impl, is_relevant_item, is_relevant_trait};
declare_clippy_lint! {
/// ### What it does
/// Checks for items annotated with `#[inline(always)]`,
/// unless the annotated function is empty or simply panics.
///
/// ### Why is this bad?
/// While there are valid uses of this annotation (and once
/// you know when to use it, by all means `allow` this lint), it's a common
/// newbie-mistake to pepper one's code with it.
///
/// As a rule of thumb, before slapping `#[inline(always)]` on a function,
/// measure if that additional function call really affects your runtime profile
/// sufficiently to make up for the increase in compile time.
///
/// ### Known problems
/// False positives, big time. This lint is meant to be
/// deactivated by everyone doing serious performance work. This means having
/// done the measurement.
///
/// ### Example
/// ```ignore
/// #[inline(always)]
/// fn not_quite_hot_code(..) { ... }
/// ```
#[clippy::version = "pre 1.29.0"]
pub INLINE_ALWAYS,
pedantic,
"use of `#[inline(always)]`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `extern crate` and `use` items annotated with
/// lint attributes.
///
/// This lint permits lint attributes for lints emitted on the items themself.
/// For `use` items these lints are:
/// * deprecated
/// * unreachable_pub
/// * unused_imports
/// * clippy::enum_glob_use
/// * clippy::macro_use_imports
/// * clippy::wildcard_imports
///
/// For `extern crate` items these lints are:
/// * `unused_imports` on items with `#[macro_use]`
///
/// ### Why is this bad?
/// Lint attributes have no effect on crate imports. Most
/// likely a `!` was forgotten.
///
/// ### Example
/// ```ignore
/// #[deny(dead_code)]
/// extern crate foo;
/// #[forbid(dead_code)]
/// use foo::bar;
/// ```
///
/// Use instead:
/// ```rust,ignore
/// #[allow(unused_imports)]
/// use foo::baz;
/// #[allow(unused_imports)]
/// #[macro_use]
/// extern crate baz;
/// ```
#[clippy::version = "pre 1.29.0"]
pub USELESS_ATTRIBUTE,
correctness,
"use of lint attributes on `extern crate` items"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `#[deprecated]` annotations with a `since`
/// field that is not a valid semantic version. Also allows "TBD" to signal
/// future deprecation.
///
/// ### Why is this bad?
/// For checking the version of the deprecation, it must be
/// a valid semver. Failing that, the contained information is useless.
///
/// ### Example
/// ```no_run
/// #[deprecated(since = "forever")]
/// fn something_else() { /* ... */ }
/// ```
#[clippy::version = "pre 1.29.0"]
pub DEPRECATED_SEMVER,
correctness,
"use of `#[deprecated(since = \"x\")]` where x is not semver"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for empty lines after outer attributes
///
/// ### Why is this bad?
/// Most likely the attribute was meant to be an inner attribute using a '!'.
/// If it was meant to be an outer attribute, then the following item
/// should not be separated by empty lines.
///
/// ### Known problems
/// Can cause false positives.
///
/// From the clippy side it's difficult to detect empty lines between an attributes and the
/// following item because empty lines and comments are not part of the AST. The parsing
/// currently works for basic cases but is not perfect.
///
/// ### Example
/// ```no_run
/// #[allow(dead_code)]
///
/// fn not_quite_good_code() { }
/// ```
///
/// Use instead:
/// ```no_run
/// // Good (as inner attribute)
/// #![allow(dead_code)]
///
/// fn this_is_fine() { }
///
/// // or
///
/// // Good (as outer attribute)
/// #[allow(dead_code)]
/// fn this_is_fine_too() { }
/// ```
#[clippy::version = "pre 1.29.0"]
pub EMPTY_LINE_AFTER_OUTER_ATTR,
nursery,
"empty line after outer attribute"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for empty lines after documentation comments.
///
/// ### Why is this bad?
/// The documentation comment was most likely meant to be an inner attribute or regular comment.
/// If it was intended to be a documentation comment, then the empty line should be removed to
/// be more idiomatic.
///
/// ### Known problems
/// Only detects empty lines immediately following the documentation. If the doc comment is followed
/// by an attribute and then an empty line, this lint will not trigger. Use `empty_line_after_outer_attr`
/// in combination with this lint to detect both cases.
///
/// Does not detect empty lines after doc attributes (e.g. `#[doc = ""]`).
///
/// ### Example
/// ```no_run
/// /// Some doc comment with a blank line after it.
///
/// fn not_quite_good_code() { }
/// ```
///
/// Use instead:
/// ```no_run
/// /// Good (no blank line)
/// fn this_is_fine() { }
/// ```
///
/// ```no_run
/// // Good (convert to a regular comment)
///
/// fn this_is_fine_too() { }
/// ```
///
/// ```no_run
/// //! Good (convert to a comment on an inner attribute)
///
/// fn this_is_fine_as_well() { }
/// ```
#[clippy::version = "1.70.0"]
pub EMPTY_LINE_AFTER_DOC_COMMENTS,
nursery,
"empty line after documentation comments"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
///
/// ### Why is this bad?
/// Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.
/// These lints should only be enabled on a lint-by-lint basis and with careful consideration.
///
/// ### Example
/// ```no_run
/// #![deny(clippy::restriction)]
/// ```
///
/// Use instead:
/// ```no_run
/// #![deny(clippy::as_conversions)]
/// ```
#[clippy::version = "1.47.0"]
pub BLANKET_CLIPPY_RESTRICTION_LINTS,
suspicious,
"enabling the complete restriction group"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
/// with `#[rustfmt::skip]`.
///
/// ### Why is this bad?
/// Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))
/// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.
///
/// ### Known problems
/// This lint doesn't detect crate level inner attributes, because they get
/// processed before the PreExpansionPass lints get executed. See
/// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
///
/// ### Example
/// ```no_run
/// #[cfg_attr(rustfmt, rustfmt_skip)]
/// fn main() { }
/// ```
///
/// Use instead:
/// ```no_run
/// #[rustfmt::skip]
/// fn main() { }
/// ```
#[clippy::version = "1.32.0"]
pub DEPRECATED_CFG_ATTR,
complexity,
"usage of `cfg_attr(rustfmt)` instead of tool attributes"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for cfg attributes having operating systems used in target family position.
///
/// ### Why is this bad?
/// The configuration option will not be recognised and the related item will not be included
/// by the conditional compilation engine.
///
/// ### Example
/// ```no_run
/// #[cfg(linux)]
/// fn conditional() { }
/// ```
///
/// Use instead:
/// ```no_run
/// # mod hidden {
/// #[cfg(target_os = "linux")]
/// fn conditional() { }
/// # }
///
/// // or
///
/// #[cfg(unix)]
/// fn conditional() { }
/// ```
/// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
#[clippy::version = "1.45.0"]
pub MISMATCHED_TARGET_OS,
correctness,
"usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for attributes that allow lints without a reason.
///
/// (This requires the `lint_reasons` feature)
///
/// ### Why is this bad?
/// Allowing a lint should always have a reason. This reason should be documented to
/// ensure that others understand the reasoning
///
/// ### Example
/// ```no_run
/// #![feature(lint_reasons)]
///
/// #![allow(clippy::some_lint)]
/// ```
///
/// Use instead:
/// ```no_run
/// #![feature(lint_reasons)]
///
/// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
/// ```
#[clippy::version = "1.61.0"]
pub ALLOW_ATTRIBUTES_WITHOUT_REASON,
restriction,
"ensures that all `allow` and `expect` attributes have a reason"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `#[should_panic]` attributes without specifying the expected panic message.
///
/// ### Why is this bad?
/// The expected panic message should be specified to ensure that the test is actually
/// panicking with the expected message, and not another unrelated panic.
///
/// ### Example
/// ```no_run
/// fn random() -> i32 { 0 }
///
/// #[should_panic]
/// #[test]
/// fn my_test() {
/// let _ = 1 / random();
/// }
/// ```
///
/// Use instead:
/// ```no_run
/// fn random() -> i32 { 0 }
///
/// #[should_panic = "attempt to divide by zero"]
/// #[test]
/// fn my_test() {
/// let _ = 1 / random();
/// }
/// ```
#[clippy::version = "1.74.0"]
pub SHOULD_PANIC_WITHOUT_EXPECT,
pedantic,
"ensures that all `should_panic` attributes specify its expected panic message"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `any` and `all` combinators in `cfg` with only one condition.
///
/// ### Why is this bad?
/// If there is only one condition, no need to wrap it into `any` or `all` combinators.
///
/// ### Example
/// ```no_run
/// #[cfg(any(unix))]
/// pub struct Bar;
/// ```
///
/// Use instead:
/// ```no_run
/// #[cfg(unix)]
/// pub struct Bar;
/// ```
#[clippy::version = "1.71.0"]
pub NON_MINIMAL_CFG,
style,
"ensure that all `cfg(any())` and `cfg(all())` have more than one condition"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `#[cfg(features = "...")]` and suggests to replace it with
/// `#[cfg(feature = "...")]`.
///
/// It also checks if `cfg(test)` was misspelled.
///
/// ### Why is this bad?
/// Misspelling `feature` as `features` or `test` as `tests` can be sometimes hard to spot. It
/// may cause conditional compilation not work quietly.
///
/// ### Example
/// ```no_run
/// #[cfg(features = "some-feature")]
/// fn conditional() { }
/// #[cfg(tests)]
/// mod tests { }
/// ```
///
/// Use instead:
/// ```no_run
/// #[cfg(feature = "some-feature")]
/// fn conditional() { }
/// #[cfg(test)]
/// mod tests { }
/// ```
#[clippy::version = "1.69.0"]
pub MAYBE_MISUSED_CFG,
suspicious,
"prevent from misusing the wrong attr name"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for
/// `#[cfg(feature = "cargo-clippy")]` and suggests to replace it with
/// `#[cfg_attr(clippy, ...)]` or `#[cfg(clippy)]`.
///
/// ### Why is this bad?
/// This feature has been deprecated for years and shouldn't be used anymore.
///
/// ### Example
/// ```no_run
/// #[cfg(feature = "cargo-clippy")]
/// struct Bar;
/// ```
///
/// Use instead:
/// ```no_run
/// #[cfg(clippy)]
/// struct Bar;
/// ```
#[clippy::version = "1.78.0"]
pub DEPRECATED_CLIPPY_CFG_ATTR,
suspicious,
"usage of `cfg(feature = \"cargo-clippy\")` instead of `cfg(clippy)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `#[cfg_attr(clippy, allow(clippy::lint))]`
/// and suggests to replace it with `#[allow(clippy::lint)]`.
///
/// ### Why is this bad?
/// There is no reason to put clippy attributes behind a clippy `cfg` as they are not
/// run by anything else than clippy.
///
/// ### Example
/// ```no_run
/// #![cfg_attr(clippy, allow(clippy::deprecated_cfg_attr))]
/// ```
///
/// Use instead:
/// ```no_run
/// #![allow(clippy::deprecated_cfg_attr)]
/// ```
#[clippy::version = "1.78.0"]
pub UNNECESSARY_CLIPPY_CFG,
suspicious,
"usage of `cfg_attr(clippy, allow(clippy::lint))` instead of `allow(clippy::lint)`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks that an item has only one kind of attributes.
///
/// ### Why is this bad?
/// Having both kinds of attributes makes it more complicated to read code.
///
/// ### Example
/// ```no_run
/// #[cfg(linux)]
/// pub fn foo() {
/// #![cfg(windows)]
/// }
/// ```
/// Use instead:
/// ```no_run
/// #[cfg(linux)]
/// #[cfg(windows)]
/// pub fn foo() {
/// }
/// ```
#[clippy::version = "1.78.0"]
pub MIXED_ATTRIBUTES_STYLE,
suspicious,
"item has both inner and outer attributes"
}
declare_lint_pass!(Attributes => [
ALLOW_ATTRIBUTES_WITHOUT_REASON,
INLINE_ALWAYS,
DEPRECATED_SEMVER,
USELESS_ATTRIBUTE,
BLANKET_CLIPPY_RESTRICTION_LINTS,
SHOULD_PANIC_WITHOUT_EXPECT,
]);
impl<'tcx> LateLintPass<'tcx> for Attributes {
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
blanket_clippy_restriction_lints::check_command_line(cx);
}
fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
if let Some(items) = &attr.meta_item_list() {
if let Some(ident) = attr.ident() {
if is_lint_level(ident.name, attr.id) {
blanket_clippy_restriction_lints::check(cx, ident.name, items);
}
if matches!(ident.name, sym::allow | sym::expect) {
allow_attributes_without_reason::check(cx, ident.name, items, attr);
}
if items.is_empty() || !attr.has_name(sym::deprecated) {
return;
}
for item in items {
if let NestedMetaItem::MetaItem(mi) = &item
&& let MetaItemKind::NameValue(lit) = &mi.kind
&& mi.has_name(sym::since)
{
deprecated_semver::check(cx, item.span(), lit);
}
}
}
}
if attr.has_name(sym::should_panic) {
should_panic_without_expect::check(cx, attr);
}
}
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
if is_relevant_item(cx, item) {
inline_always::check(cx, item.span, item.ident.name, attrs);
}
match item.kind {
ItemKind::ExternCrate(..) | ItemKind::Use(..) => useless_attribute::check(cx, item, attrs),
_ => {},
}
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
if is_relevant_impl(cx, item) {
inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
}
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if is_relevant_trait(cx, item) {
inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
}
}
}
pub struct EarlyAttributes {
pub msrv: Msrv,
}
impl_lint_pass!(EarlyAttributes => [
DEPRECATED_CFG_ATTR,
MISMATCHED_TARGET_OS,
EMPTY_LINE_AFTER_OUTER_ATTR,
EMPTY_LINE_AFTER_DOC_COMMENTS,
NON_MINIMAL_CFG,
MAYBE_MISUSED_CFG,
DEPRECATED_CLIPPY_CFG_ATTR,
UNNECESSARY_CLIPPY_CFG,
MIXED_ATTRIBUTES_STYLE,
]);
impl EarlyLintPass for EarlyAttributes {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
empty_line_after::check(cx, item);
mixed_attributes_style::check(cx, item);
}
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
deprecated_cfg_attr::check(cx, attr, &self.msrv);
deprecated_cfg_attr::check_clippy(cx, attr);
mismatched_target_os::check(cx, attr);
non_minimal_cfg::check(cx, attr);
maybe_misused_cfg::check(cx, attr);
}
extract_msrv_attr!(EarlyContext);
}

View file

@ -0,0 +1,49 @@
use super::{Attribute, NON_MINIMAL_CFG};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use rustc_ast::{MetaItemKind, NestedMetaItem};
use rustc_errors::Applicability;
use rustc_lint::EarlyContext;
use rustc_span::sym;
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
if attr.has_name(sym::cfg)
&& let Some(items) = attr.meta_item_list()
{
check_nested_cfg(cx, &items);
}
}
fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
for item in items {
if let NestedMetaItem::MetaItem(meta) = item {
if !meta.has_name(sym::any) && !meta.has_name(sym::all) {
continue;
}
if let MetaItemKind::List(list) = &meta.kind {
check_nested_cfg(cx, list);
if list.len() == 1 {
span_lint_and_then(
cx,
NON_MINIMAL_CFG,
meta.span,
"unneeded sub `cfg` when there is only one condition",
|diag| {
if let Some(snippet) = snippet_opt(cx, list[0].span()) {
diag.span_suggestion(meta.span, "try", snippet, Applicability::MaybeIncorrect);
}
},
);
} else if list.is_empty() && meta.has_name(sym::all) {
span_lint_and_then(
cx,
NON_MINIMAL_CFG,
meta.span,
"unneeded sub `cfg` when there is no condition",
|_| {},
);
}
}
}
}
}

View file

@ -0,0 +1,54 @@
use super::{Attribute, SHOULD_PANIC_WITHOUT_EXPECT};
use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_ast::token::{Token, TokenKind};
use rustc_ast::tokenstream::TokenTree;
use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind};
use rustc_errors::Applicability;
use rustc_lint::LateContext;
use rustc_span::sym;
pub(super) fn check(cx: &LateContext<'_>, attr: &Attribute) {
if let AttrKind::Normal(normal_attr) = &attr.kind {
if let AttrArgs::Eq(_, AttrArgsEq::Hir(_)) = &normal_attr.item.args {
// `#[should_panic = ".."]` found, good
return;
}
if let AttrArgs::Delimited(args) = &normal_attr.item.args
&& let mut tt_iter = args.tokens.trees()
&& let Some(TokenTree::Token(
Token {
kind: TokenKind::Ident(sym::expected, _),
..
},
_,
)) = tt_iter.next()
&& let Some(TokenTree::Token(
Token {
kind: TokenKind::Eq, ..
},
_,
)) = tt_iter.next()
&& let Some(TokenTree::Token(
Token {
kind: TokenKind::Literal(_),
..
},
_,
)) = tt_iter.next()
{
// `#[should_panic(expected = "..")]` found, good
return;
}
span_lint_and_sugg(
cx,
SHOULD_PANIC_WITHOUT_EXPECT,
attr.span,
"#[should_panic] attribute without a reason",
"consider specifying the expected panic",
"#[should_panic(expected = /* panic message */)]".into(),
Applicability::HasPlaceholders,
);
}
}

View file

@ -0,0 +1,70 @@
use super::{Attribute, UNNECESSARY_CLIPPY_CFG};
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
use clippy_utils::source::snippet_opt;
use rustc_ast::AttrStyle;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, Level};
use rustc_span::sym;
pub(super) fn check(
cx: &EarlyContext<'_>,
cfg_attr: &rustc_ast::MetaItem,
behind_cfg_attr: &rustc_ast::MetaItem,
attr: &Attribute,
) {
if cfg_attr.has_name(sym::clippy)
&& let Some(ident) = behind_cfg_attr.ident()
&& Level::from_symbol(ident.name, Some(attr.id)).is_some()
&& let Some(items) = behind_cfg_attr.meta_item_list()
{
let nb_items = items.len();
let mut clippy_lints = Vec::with_capacity(items.len());
for item in items {
if let Some(meta_item) = item.meta_item()
&& let [part1, _] = meta_item.path.segments.as_slice()
&& part1.ident.name == sym::clippy
{
clippy_lints.push(item.span());
}
}
if clippy_lints.is_empty() {
return;
}
if nb_items == clippy_lints.len() {
if let Some(snippet) = snippet_opt(cx, behind_cfg_attr.span) {
span_lint_and_sugg(
cx,
UNNECESSARY_CLIPPY_CFG,
attr.span,
"no need to put clippy lints behind a `clippy` cfg",
"replace with",
format!(
"#{}[{}]",
if attr.style == AttrStyle::Inner { "!" } else { "" },
snippet
),
Applicability::MachineApplicable,
);
}
} else {
let snippet = clippy_lints
.iter()
.filter_map(|sp| snippet_opt(cx, *sp))
.collect::<Vec<_>>()
.join(",");
span_lint_and_note(
cx,
UNNECESSARY_CLIPPY_CFG,
clippy_lints,
"no need to put clippy lints behind a `clippy` cfg",
None,
&format!(
"write instead: `#{}[{}({})]`",
if attr.style == AttrStyle::Inner { "!" } else { "" },
ident.name,
snippet
),
);
}
}
}

View file

@ -0,0 +1,73 @@
use super::utils::{extract_clippy_lint, is_lint_level, is_word};
use super::{Attribute, USELESS_ATTRIBUTE};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{first_line_of_span, snippet_opt};
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_span::sym;
pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) {
let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use));
for attr in attrs {
if in_external_macro(cx.sess(), attr.span) {
return;
}
if let Some(lint_list) = &attr.meta_item_list() {
if attr.ident().map_or(false, |ident| is_lint_level(ident.name, attr.id)) {
for lint in lint_list {
match item.kind {
ItemKind::Use(..) => {
if is_word(lint, sym::unused_imports)
|| is_word(lint, sym::deprecated)
|| is_word(lint, sym!(unreachable_pub))
|| is_word(lint, sym!(unused))
|| is_word(lint, sym!(unused_import_braces))
|| extract_clippy_lint(lint).map_or(false, |s| {
matches!(
s.as_str(),
"wildcard_imports"
| "enum_glob_use"
| "redundant_pub_crate"
| "macro_use_imports"
| "unsafe_removed_from_name"
| "module_name_repetitions"
| "single_component_path_imports"
)
})
{
return;
}
},
ItemKind::ExternCrate(..) => {
if is_word(lint, sym::unused_imports) && skip_unused_imports {
return;
}
if is_word(lint, sym!(unused_extern_crates)) {
return;
}
},
_ => {},
}
}
let line_span = first_line_of_span(cx, attr.span);
if let Some(mut sugg) = snippet_opt(cx, line_span) {
if sugg.contains("#[") {
span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| {
sugg = sugg.replacen("#[", "#![", 1);
diag.span_suggestion(
line_span,
"if you just forgot a `!`, use",
sugg,
Applicability::MaybeIncorrect,
);
});
}
}
}
}
}
}

View file

@ -0,0 +1,87 @@
use clippy_utils::macros::{is_panic, macro_backtrace};
use rustc_ast::{AttrId, NestedMetaItem};
use rustc_hir::{
Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
};
use rustc_lint::{LateContext, Level};
use rustc_middle::ty;
use rustc_span::sym;
use rustc_span::symbol::Symbol;
pub(super) fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
if let NestedMetaItem::MetaItem(mi) = &nmi {
mi.is_word() && mi.has_name(expected)
} else {
false
}
}
pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool {
Level::from_symbol(symbol, Some(attr_id)).is_some()
}
pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
if let ItemKind::Fn(_, _, eid) = item.kind {
is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value)
} else {
true
}
}
pub(super) fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool {
match item.kind {
ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value),
_ => false,
}
}
pub(super) fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool {
match item.kind {
TraitItemKind::Fn(_, TraitFn::Required(_)) => true,
TraitItemKind::Fn(_, TraitFn::Provided(eid)) => {
is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value)
},
_ => false,
}
}
fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool {
block.stmts.first().map_or(
block
.expr
.as_ref()
.map_or(false, |e| is_relevant_expr(cx, typeck_results, e)),
|stmt| match &stmt.kind {
StmtKind::Local(_) => true,
StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
StmtKind::Item(_) => false,
},
)
}
fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool {
if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable
}) {
return false;
}
match &expr.kind {
ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block),
ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e),
ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
_ => true,
}
}
/// Returns the lint name if it is clippy lint.
pub(super) fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<Symbol> {
if let Some(meta_item) = lint.meta_item()
&& meta_item.path.segments.len() > 1
&& let tool_name = meta_item.path.segments[0].ident
&& tool_name.name == sym::clippy
{
let lint_name = meta_item.path.segments.last().unwrap().ident.name;
return Some(lint_name);
}
None
}

View file

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

View file

@ -151,13 +151,24 @@ pub(super) fn check<'tcx>(
return false;
}
// If the whole cast expression is a unary expression (`(*x as T)`) or an addressof
// expression (`(&x as T)`), then not surrounding the suggestion into a block risks us
// changing the precedence of operators if the cast expression is followed by an operation
// with higher precedence than the unary operator (`(*x as T).foo()` would become
// `*x.foo()`, which changes what the `*` applies on).
// The same is true if the expression encompassing the cast expression is a unary
// expression or an addressof expression.
let needs_block = matches!(cast_expr.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..))
|| get_parent_expr(cx, expr)
.map_or(false, |e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)));
span_lint_and_sugg(
cx,
UNNECESSARY_CAST,
expr.span,
&format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"),
"try",
if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(..))) {
if needs_block {
format!("{{ {cast_str} }}")
} else {
cast_str

View file

@ -47,6 +47,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX_INFO,
crate::assertions_on_constants::ASSERTIONS_ON_CONSTANTS_INFO,
crate::assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES_INFO,
crate::assigning_clones::ASSIGNING_CLONES_INFO,
crate::async_yields_async::ASYNC_YIELDS_ASYNC_INFO,
crate::attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON_INFO,
crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO,
@ -58,6 +59,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::attrs::INLINE_ALWAYS_INFO,
crate::attrs::MAYBE_MISUSED_CFG_INFO,
crate::attrs::MISMATCHED_TARGET_OS_INFO,
crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO,
crate::attrs::NON_MINIMAL_CFG_INFO,
crate::attrs::SHOULD_PANIC_WITHOUT_EXPECT_INFO,
crate::attrs::UNNECESSARY_CLIPPY_CFG_INFO,

View file

@ -451,8 +451,8 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r
&& let Some(def_id) = trait_ref.trait_def_id()
&& cx.tcx.is_diagnostic_item(sym::PartialEq, def_id)
&& !has_non_exhaustive_attr(cx.tcx, *adt)
&& !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id)
&& let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id)
&& !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[])
// If all of our fields implement `Eq`, we can implement `Eq` too
&& adt
.all_fields()
@ -471,6 +471,10 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r
}
}
fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: DefId) -> bool {
tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some()
}
/// Creates the `ParamEnv` used for the give type's derived `Eq` impl.
fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv<'_> {
// Initial map from generic index to param def.

View file

@ -8,7 +8,7 @@ use url::Url;
use crate::doc::DOC_MARKDOWN;
pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span, code_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 +46,11 @@ pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str,
span.parent(),
);
check_word(cx, word, span);
check_word(cx, word, span, code_level);
}
}
fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_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).
@ -60,7 +60,14 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
return false;
}
let s = s.strip_suffix('s').unwrap_or(s);
let s = if let Some(prefix) = s.strip_suffix("es")
&& prefix.chars().all(|c| c.is_ascii_uppercase())
&& matches!(prefix.chars().last(), Some('S' | 'X'))
{
prefix
} else {
s.strip_suffix('s').unwrap_or(s)
};
s.chars().all(char::is_alphanumeric)
&& s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
@ -90,7 +97,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
}
// We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343)
if has_underscore(word) && has_hyphen(word) {
if code_level > 0 || (has_underscore(word) && has_hyphen(word)) {
return;
}

View file

@ -599,10 +599,19 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
let mut ignore = false;
let mut edition = None;
let mut ticks_unbalanced = false;
let mut text_to_check: Vec<(CowStr<'_>, Range<usize>)> = Vec::new();
let mut text_to_check: Vec<(CowStr<'_>, Range<usize>, isize)> = Vec::new();
let mut paragraph_range = 0..0;
let mut code_level = 0;
for (event, range) in events {
match event {
Html(tag) => {
if tag.starts_with("<code") {
code_level += 1;
} else if tag.starts_with("</code") {
code_level -= 1;
}
},
Start(CodeBlock(ref kind)) => {
in_code = true;
if let CodeBlockKind::Fenced(lang) = kind {
@ -652,16 +661,15 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
"a backtick may be missing a pair",
);
} else {
for (text, range) in text_to_check {
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);
markdown::check(cx, valid_idents, &text, span, assoc_code_level);
}
}
}
text_to_check = Vec::new();
},
Start(_tag) | End(_tag) => (), // We don't care about other tags
Html(_html) => (), // HTML is weird, just ignore it
SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
FootnoteReference(text) | Text(text) => {
paragraph_range.end = range.end;
@ -694,7 +702,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
// Don't check the text associated with external URLs
continue;
}
text_to_check.push((text, range));
text_to_check.push((text, range, code_level));
}
},
}

View file

@ -214,12 +214,31 @@ impl MapType {
}
}
/// Details on an expression checking whether a map contains a key.
///
/// For instance, with the following:
/// ```ignore
/// !!!self.the_map.contains_key("the_key")
/// ```
///
/// - `negated` will be set to `true` (the 3 `!` negate the condition)
/// - `map` will be the `self.the_map` expression
/// - `key` will be the `"the_key"` expression
struct ContainsExpr<'tcx> {
/// Whether the check for `contains_key` was negated.
negated: bool,
/// The map on which the check is performed.
map: &'tcx Expr<'tcx>,
/// The key that is checked to be contained.
key: &'tcx Expr<'tcx>,
/// The context of the whole condition expression.
call_ctxt: SyntaxContext,
}
/// Inspect the given expression and return details about the `contains_key` check.
///
/// If the given expression is not a `contains_key` check against a `BTreeMap` or a `HashMap`,
/// return `None`.
fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
let mut negated = false;
let expr = peel_hir_expr_while(expr, |e| match e.kind {
@ -229,6 +248,7 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio
},
_ => None,
});
match expr.kind {
ExprKind::MethodCall(
_,
@ -261,11 +281,28 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio
}
}
/// Details on an expression inserting a key into a map.
///
/// For instance, on the following:
/// ```ignore
/// self.the_map.insert("the_key", 3 + 4);
/// ```
///
/// - `map` will be the `self.the_map` expression
/// - `key` will be the `"the_key"` expression
/// - `value` will be the `3 + 4` expression
struct InsertExpr<'tcx> {
/// The map into which the insertion is performed.
map: &'tcx Expr<'tcx>,
/// The key at which to insert.
key: &'tcx Expr<'tcx>,
/// The value to insert.
value: &'tcx Expr<'tcx>,
}
/// Inspect the given expression and return details about the `insert` call.
///
/// If the given expression is not an `insert` call into a `BTreeMap` or a `HashMap`, return `None`.
fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
if let ExprKind::MethodCall(_, map, [key, value], _) = expr.kind {
let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
@ -298,7 +335,7 @@ struct Insertion<'tcx> {
value: &'tcx Expr<'tcx>,
}
/// This visitor needs to do a multiple things:
/// This visitor needs to do multiple things:
/// * Find all usages of the map. An insertion can only be made before any other usages of the map.
/// * Determine if there's an insertion using the same key. There's no need for the entry api
/// otherwise.
@ -346,7 +383,7 @@ impl<'tcx> InsertSearcher<'_, 'tcx> {
res
}
/// Visits an expression which is not itself in a tail position, but other sibling expressions
/// Visit an expression which is not itself in a tail position, but other sibling expressions
/// may be. e.g. if conditions
fn visit_non_tail_expr(&mut self, e: &'tcx Expr<'_>) {
let in_tail_pos = self.in_tail_pos;
@ -354,6 +391,19 @@ impl<'tcx> InsertSearcher<'_, 'tcx> {
self.visit_expr(e);
self.in_tail_pos = in_tail_pos;
}
/// Visit the key and value expression of an insert expression.
/// There may not be uses of the map in either of those two either.
fn visit_insert_expr_arguments(&mut self, e: &InsertExpr<'tcx>) {
let in_tail_pos = self.in_tail_pos;
let allow_insert_closure = self.allow_insert_closure;
let is_single_insert = self.is_single_insert;
walk_expr(self, e.key);
walk_expr(self, e.value);
self.in_tail_pos = in_tail_pos;
self.allow_insert_closure = allow_insert_closure;
self.is_single_insert = is_single_insert;
}
}
impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
@ -425,6 +475,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
match try_parse_insert(self.cx, expr) {
Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => {
self.visit_insert_expr_arguments(&insert_expr);
// Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api.
if self.is_map_used
|| !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key)

View file

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

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths};
use rustc_hir::{Local, PatKind};
use rustc_hir::{Local, LocalSource, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{GenericArgKind, IsSuggestable};
@ -139,7 +139,8 @@ const SYNC_GUARD_PATHS: [&[&str]; 3] = [
impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &Local<'tcx>) {
if !in_external_macro(cx.tcx.sess, local.span)
if matches!(local.source, LocalSource::Normal)
&& !in_external_macro(cx.tcx.sess, local.span)
&& let PatKind::Wild = local.pat.kind
&& let Some(init) = local.init
{

View file

@ -80,6 +80,7 @@ mod as_conversions;
mod asm_syntax;
mod assertions_on_constants;
mod assertions_on_result_states;
mod assigning_clones;
mod async_yields_async;
mod attrs;
mod await_holding_invalid;
@ -1118,6 +1119,7 @@ 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));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View file

@ -3,6 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_copy;
use clippy_utils::usage::local_used_in;
use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg};
use rustc_ast::ast;
use rustc_errors::Applicability;
@ -63,8 +64,9 @@ pub(super) fn check<'tcx>(
&& get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some()
&& let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts)
&& let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts)
// Source and destination must be different
&& !local_used_in(cx, canonical_id, base_left)
&& !local_used_in(cx, canonical_id, base_right)
// Source and destination must be different
&& path_to_local(base_left) != path_to_local(base_right)
{
Some((

View file

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

View file

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

View file

@ -3875,6 +3875,7 @@ declare_clippy_lint! {
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `option.map(f).unwrap_or_default()` and `result.map(f).unwrap_or_default()` where f is a function or closure that returns the `bool` type.
///
/// ### Why is this bad?
@ -3981,6 +3982,7 @@ declare_clippy_lint! {
}
declare_clippy_lint! {
/// ### What it does
/// Checks for the manual creation of C strings (a string with a `NUL` byte at the end), either
/// through one of the `CStr` constructor functions, or more plainly by calling `.as_ptr()`
/// on a (byte) string literal with a hardcoded `\0` byte at the end.

View file

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

View file

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

View file

@ -42,7 +42,7 @@ pub(crate) fn check<'tcx>(
match op {
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
check_op(
let _ = check_op(
cx,
left,
0,
@ -50,8 +50,7 @@ pub(crate) fn check<'tcx>(
peeled_right_span,
needs_parenthesis(cx, expr, right),
right_is_coerced_to_value,
);
check_op(
) || check_op(
cx,
right,
0,
@ -62,7 +61,7 @@ pub(crate) fn check<'tcx>(
);
},
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
check_op(
let _ = check_op(
cx,
right,
0,
@ -73,7 +72,7 @@ pub(crate) fn check<'tcx>(
);
},
BinOpKind::Mul => {
check_op(
let _ = check_op(
cx,
left,
1,
@ -81,8 +80,18 @@ pub(crate) fn check<'tcx>(
peeled_right_span,
needs_parenthesis(cx, expr, right),
right_is_coerced_to_value,
);
check_op(
) || check_op(
cx,
right,
1,
expr.span,
peeled_left_span,
Parens::Unneeded,
left_is_coerced_to_value,
);
},
BinOpKind::Div => {
let _ = check_op(
cx,
right,
1,
@ -92,17 +101,8 @@ pub(crate) fn check<'tcx>(
left_is_coerced_to_value,
);
},
BinOpKind::Div => check_op(
cx,
right,
1,
expr.span,
peeled_left_span,
Parens::Unneeded,
left_is_coerced_to_value,
),
BinOpKind::BitAnd => {
check_op(
let _ = check_op(
cx,
left,
-1,
@ -110,8 +110,7 @@ pub(crate) fn check<'tcx>(
peeled_right_span,
needs_parenthesis(cx, expr, right),
right_is_coerced_to_value,
);
check_op(
) || check_op(
cx,
right,
-1,
@ -201,12 +200,12 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
}
}
fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) {
fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) -> bool {
if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
ty::Uint(uty) => clip(cx.tcx, !0, uty),
_ => return,
_ => return false,
};
if match m {
0 => v == 0,
@ -215,8 +214,10 @@ fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, pa
_ => unreachable!(),
} {
span_ineffective_operation(cx, span, arg, parens, is_erased);
return true;
}
}
false
}
fn span_ineffective_operation(

View file

@ -21,9 +21,8 @@ pub(super) fn check<'tcx>(
// lhs op= l op r
if eq_expr_value(cx, lhs, l) {
lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, r);
}
// lhs op= l commutative_op r
if is_commutative(op) && eq_expr_value(cx, lhs, r) {
} else if is_commutative(op) && eq_expr_value(cx, lhs, r) {
// lhs op= l commutative_op r
lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, l);
}
}

View file

@ -203,109 +203,107 @@ fn expr_return_none_or_err(
}
}
/// Checks if the given expression on the given context matches the following structure:
///
/// ```ignore
/// if option.is_none() {
/// return None;
/// }
/// ```
///
/// ```ignore
/// if result.is_err() {
/// return result;
/// }
/// ```
///
/// If it matches, it will suggest to use the question mark operator instead
fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr)
&& !is_else_clause(cx.tcx, expr)
&& let ExprKind::MethodCall(segment, caller, ..) = &cond.kind
&& let caller_ty = cx.typeck_results().expr_ty(caller)
&& let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then)
&& (is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block))
{
let mut applicability = Applicability::MachineApplicable;
let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env)
&& !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
let sugg = if let Some(else_inner) = r#else {
if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
format!("Some({receiver_str}?)")
} else {
return;
}
} else {
format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" })
};
span_lint_and_sugg(
cx,
QUESTION_MARK,
expr.span,
"this block may be rewritten with the `?` operator",
"replace it with",
sugg,
applicability,
);
}
}
fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if let Some(higher::IfLet {
let_pat,
let_expr,
if_then,
if_else,
..
}) = higher::IfLet::hir(cx, expr)
&& !is_else_clause(cx.tcx, expr)
&& let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind
&& ddpos.as_opt_usize().is_none()
&& let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind
&& let caller_ty = cx.typeck_results().expr_ty(let_expr)
&& let if_block = IfBlockType::IfLet(
cx.qpath_res(path1, let_pat.hir_id),
caller_ty,
ident.name,
let_expr,
if_then,
if_else,
)
&& ((is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
|| is_early_return(sym::Result, cx, &if_block))
&& if_else
.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e)))
.filter(|e| *e)
.is_none()
{
let mut applicability = Applicability::MachineApplicable;
let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_));
let sugg = format!(
"{receiver_str}{}?{}",
if by_ref == ByRef::Yes { ".as_ref()" } else { "" },
if requires_semi { ";" } else { "" }
);
span_lint_and_sugg(
cx,
QUESTION_MARK,
expr.span,
"this block may be rewritten with the `?` operator",
"replace it with",
sugg,
applicability,
);
}
}
impl QuestionMark {
fn inside_try_block(&self) -> bool {
self.try_block_depth_stack.last() > Some(&0)
}
/// Checks if the given expression on the given context matches the following structure:
///
/// ```ignore
/// if option.is_none() {
/// return None;
/// }
/// ```
///
/// ```ignore
/// if result.is_err() {
/// return result;
/// }
/// ```
///
/// If it matches, it will suggest to use the question mark operator instead
fn check_is_none_or_err_and_early_return<'tcx>(&self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if !self.inside_try_block()
&& let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr)
&& !is_else_clause(cx.tcx, expr)
&& let ExprKind::MethodCall(segment, caller, ..) = &cond.kind
&& let caller_ty = cx.typeck_results().expr_ty(caller)
&& let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then)
&& (is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block))
{
let mut applicability = Applicability::MachineApplicable;
let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env)
&& !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
let sugg = if let Some(else_inner) = r#else {
if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
format!("Some({receiver_str}?)")
} else {
return;
}
} else {
format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" })
};
span_lint_and_sugg(
cx,
QUESTION_MARK,
expr.span,
"this block may be rewritten with the `?` operator",
"replace it with",
sugg,
applicability,
);
}
}
fn check_if_let_some_or_err_and_early_return<'tcx>(&self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if !self.inside_try_block()
&& let Some(higher::IfLet {
let_pat,
let_expr,
if_then,
if_else,
..
}) = higher::IfLet::hir(cx, expr)
&& !is_else_clause(cx.tcx, expr)
&& let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind
&& ddpos.as_opt_usize().is_none()
&& let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind
&& let caller_ty = cx.typeck_results().expr_ty(let_expr)
&& let if_block = IfBlockType::IfLet(
cx.qpath_res(path1, let_pat.hir_id),
caller_ty,
ident.name,
let_expr,
if_then,
if_else,
)
&& ((is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
|| is_early_return(sym::Result, cx, &if_block))
&& if_else
.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e)))
.filter(|e| *e)
.is_none()
{
let mut applicability = Applicability::MachineApplicable;
let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_));
let sugg = format!(
"{receiver_str}{}?{}",
if by_ref == ByRef::Yes { ".as_ref()" } else { "" },
if requires_semi { ";" } else { "" }
);
span_lint_and_sugg(
cx,
QUESTION_MARK,
expr.span,
"this block may be rewritten with the `?` operator",
"replace it with",
sugg,
applicability,
);
}
}
}
fn is_try_block(cx: &LateContext<'_>, bl: &rustc_hir::Block<'_>) -> bool {
@ -324,15 +322,18 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark {
return;
}
if !in_constant(cx, stmt.hir_id) {
if !self.inside_try_block() && !in_constant(cx, stmt.hir_id) {
check_let_some_else_return_none(cx, stmt);
}
self.check_manual_let_else(cx, stmt);
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !in_constant(cx, expr.hir_id) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) {
self.check_is_none_or_err_and_early_return(cx, expr);
self.check_if_let_some_or_err_and_early_return(cx, expr);
if !self.inside_try_block()
&& !in_constant(cx, expr.hir_id)
&& is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id)
{
check_is_none_or_err_and_early_return(cx, expr);
check_if_let_some_or_err_and_early_return(cx, expr);
}
}

View file

@ -159,6 +159,15 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
// ^^ we only want to lint for this call (but we walk up the calls to consider both calls).
// without this check, we'd end up linting twice.
&& !matches!(recv.kind, hir::ExprKind::Call(..))
// Check if `recv` comes from a macro expansion. If it does, make sure that it's an expansion that is
// the same as the one the call is in.
// For instance, let's assume `x!()` returns a closure:
// B ---v
// x!()()
// ^- A
// The call happens in the expansion `A`, while the closure originates from the expansion `B`.
// We don't want to suggest replacing `x!()()` with `x!()`.
&& recv.span.ctxt().outer_expn() == expr.span.ctxt().outer_expn()
&& let (full_expr, call_depth) = get_parent_call_exprs(cx, expr)
&& let Some((body, fn_decl, coroutine_kind, params)) = find_innermost_closure(cx, recv, call_depth)
// outside macros we lint properly. Inside macros, we lint only ||() style closures.

View file

@ -59,24 +59,22 @@ impl EarlyLintPass for RedundantFieldNames {
}
if let ExprKind::Struct(ref se) = expr.kind {
for field in &se.fields {
if field.is_shorthand {
continue;
}
if let ExprKind::Path(None, path) = &field.expr.kind {
if path.segments.len() == 1
&& path.segments[0].ident == field.ident
&& path.segments[0].args.is_none()
{
span_lint_and_sugg(
cx,
REDUNDANT_FIELD_NAMES,
field.span,
"redundant field names in struct initialization",
"replace it with",
field.ident.to_string(),
Applicability::MachineApplicable,
);
}
if !field.is_shorthand
&& let ExprKind::Path(None, path) = &field.expr.kind
&& let [segment] = path.segments.as_slice()
&& segment.args.is_none()
&& segment.ident == field.ident
&& field.span.eq_ctxt(field.ident.span)
{
span_lint_and_sugg(
cx,
REDUNDANT_FIELD_NAMES,
field.span,
"redundant field names in struct initialization",
"replace it with",
field.ident.to_string(),
Applicability::MachineApplicable,
);
}
}
}

View file

@ -109,7 +109,6 @@ 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 = path.span;
return;
},
},
@ -117,13 +116,12 @@ 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 = path.span;
return;
}
},
_ => return,
};
if path.span != self.prev_span {
if first_segment.ident.span != self.prev_span {
span_lint_and_sugg(
cx,
lint,
@ -133,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
replace_with.to_string(),
Applicability::MachineApplicable,
);
self.prev_span = path.span;
self.prev_span = first_segment.ident.span;
}
}
}

View file

@ -1,12 +1,11 @@
use clippy_config::msrvs::Msrv;
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::fn_has_unsatisfiable_preds;
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
use clippy_utils::source::snippet;
use clippy_utils::{fn_has_unsatisfiable_preds, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir::{intravisit, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
use rustc_span::sym::thread_local_macro;
@ -57,6 +56,31 @@ impl ThreadLocalInitializerCanBeMadeConst {
impl_lint_pass!(ThreadLocalInitializerCanBeMadeConst => [THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST]);
#[inline]
fn is_thread_local_initializer(
cx: &LateContext<'_>,
fn_kind: rustc_hir::intravisit::FnKind<'_>,
span: rustc_span::Span,
) -> Option<bool> {
let macro_def_id = span.source_callee()?.macro_def_id?;
Some(
cx.tcx.is_diagnostic_item(thread_local_macro, macro_def_id)
&& matches!(fn_kind, intravisit::FnKind::ItemFn(..)),
)
}
#[inline]
fn initializer_can_be_made_const(cx: &LateContext<'_>, defid: rustc_span::def_id::DefId, msrv: &Msrv) -> bool {
// Building MIR for `fn`s with unsatisfiable preds results in ICE.
if !fn_has_unsatisfiable_preds(cx, defid)
&& let mir = cx.tcx.optimized_mir(defid)
&& let Ok(()) = is_min_const_fn(cx.tcx, mir, msrv)
{
return true;
}
false
}
impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
fn check_fn(
&mut self,
@ -65,31 +89,32 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
_: &'tcx rustc_hir::FnDecl<'tcx>,
body: &'tcx rustc_hir::Body<'tcx>,
span: rustc_span::Span,
defid: rustc_span::def_id::LocalDefId,
local_defid: rustc_span::def_id::LocalDefId,
) {
if in_external_macro(cx.sess(), span)
&& let Some(callee) = span.source_callee()
&& let Some(macro_def_id) = callee.macro_def_id
&& cx.tcx.is_diagnostic_item(thread_local_macro, macro_def_id)
&& let intravisit::FnKind::ItemFn(..) = fn_kind
// Building MIR for `fn`s with unsatisfiable preds results in ICE.
&& !fn_has_unsatisfiable_preds(cx, defid.to_def_id())
&& let mir = cx.tcx.optimized_mir(defid.to_def_id())
&& let Ok(()) = is_min_const_fn(cx.tcx, mir, &self.msrv)
// this is the `__init` function emitted by the `thread_local!` macro
// when the `const` keyword is not used. We avoid checking the `__init` directly
// as that is not a public API.
// we know that the function is const-qualifiable, so now we need only to get the
// initializer expression to span-lint it.
let defid = local_defid.to_def_id();
if self.msrv.meets(msrvs::THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST)
&& is_thread_local_initializer(cx, fn_kind, span).unwrap_or(false)
// Some implementations of `thread_local!` include an initializer fn.
// In the case of a const initializer, the init fn is also const,
// so we can skip the lint in that case. This occurs only on some
// backends due to conditional compilation:
// https://doc.rust-lang.org/src/std/sys/common/thread_local/mod.rs.html
// for details on this issue, see:
// https://github.com/rust-lang/rust-clippy/pull/12276
&& !cx.tcx.is_const_fn(defid)
&& initializer_can_be_made_const(cx, defid, &self.msrv)
// we know that the function is const-qualifiable, so now
// we need only to get the initializer expression to span-lint it.
&& let ExprKind::Block(block, _) = body.value.kind
&& let Some(ret_expr) = block.expr
&& let Some(unpeeled) = block.expr
&& let ret_expr = peel_blocks(unpeeled)
&& let initializer_snippet = snippet(cx, ret_expr.span, "thread_local! { ... }")
&& initializer_snippet != "thread_local! { ... }"
{
span_lint_and_sugg(
cx,
THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST,
ret_expr.span,
unpeeled.span,
"initializer for `thread_local` value can be made `const`",
"replace with",
format!("const {{ {initializer_snippet} }}"),

View file

@ -592,7 +592,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
| (eager_transmute::check(cx, e, arg, from_ty, to_ty));
if !linted {
transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, from_ty_adjusted, to_ty, arg);
transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, from_ty_adjusted, to_ty, arg, const_context);
}
}
}

View file

@ -18,10 +18,12 @@ pub(super) fn check<'tcx>(
from_ty_adjusted: bool,
to_ty: Ty<'tcx>,
arg: &'tcx Expr<'_>,
const_context: bool,
) -> bool {
use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
let mut app = Applicability::MachineApplicable;
let mut sugg = match check_cast(cx, e, from_ty, to_ty) {
Some(FnPtrAddrCast | PtrAddrCast) if const_context => return false,
Some(PtrPtrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) => {
Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app)
.as_ty(to_ty.to_string())

View file

@ -1,10 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::paths::ALLOCATOR_GLOBAL;
use clippy_utils::last_path_segment;
use clippy_utils::source::snippet;
use clippy_utils::{last_path_segment, match_def_path};
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, GenericArg, QPath, TyKind};
use rustc_hir::{self as hir, GenericArg, LangItem, QPath, TyKind};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::LateContext;
use rustc_middle::ty::layout::LayoutOf;
@ -50,7 +49,7 @@ pub(super) fn check<'tcx>(
(None, Some(GenericArg::Type(inner))) | (Some(GenericArg::Type(inner)), None) => {
if let TyKind::Path(path) = inner.kind
&& let Some(did) = cx.qpath_res(&path, inner.hir_id).opt_def_id() {
match_def_path(cx, did, &ALLOCATOR_GLOBAL)
cx.tcx.lang_items().get(LangItem::GlobalAlloc) == Some(did)
} else {
false
}

View file

@ -115,4 +115,3 @@ pub const OPTION_UNWRAP: [&str; 4] = ["core", "option", "Option", "unwrap"];
pub const OPTION_EXPECT: [&str; 4] = ["core", "option", "Option", "expect"];
#[expect(clippy::invalid_paths)] // not sure why it thinks this, it works so
pub const BOOL_THEN: [&str; 4] = ["core", "bool", "<impl bool>", "then"];
pub const ALLOCATOR_GLOBAL: [&str; 3] = ["alloc", "alloc", "Global"];

View file

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

View file

@ -995,7 +995,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
// no adjustment needed here, as field projections are handled by the compiler
ProjectionKind::Field(..) => match cmt.place.ty_before_projection(i).kind() {
ty::Adt(..) | ty::Tuple(_) => {
replacement_str = ident_str_with_proj.clone();
replacement_str.clone_from(&ident_str_with_proj);
projections_handled = true;
},
_ => (),

View file

@ -174,8 +174,13 @@ To allow or deny a lint from the command line you can use <cyan,bold>cargo clipp
You can use tool lints to allow or deny lints from your code, e.g.:
<yellow,bold>#[allow(clippy::needless_lifetimes)]</>
"
)
<green,bold>Manifest Options:</>
<cyan,bold>--manifest-path</> <cyan><<PATH>></> Path to Cargo.toml
<cyan,bold>--frozen</> Require Cargo.lock and cache are up to date
<cyan,bold>--locked</> Require Cargo.lock is up to date
<cyan,bold>--offline</> Run without accessing the network
")
}
#[cfg(test)]
mod tests {

View file

@ -138,6 +138,7 @@ fn base_config(test_dir: &str) -> (Config, Args) {
"-Aunused",
"-Ainternal_features",
"-Zui-testing",
"-Zdeduplicate-diagnostics=no",
"-Dwarnings",
&format!("-Ldependency={}", deps_path.display()),
]

View file

@ -54,7 +54,7 @@ fn explore_directory(dir: &Path) -> Vec<String> {
let file_prefix = path.file_prefix().unwrap().to_str().unwrap().to_string();
if let Some(ext) = path.extension() {
match ext.to_str().unwrap() {
"rs" | "toml" => current_file = file_prefix.clone(),
"rs" | "toml" => current_file.clone_from(&file_prefix),
"stderr" | "stdout" => {
if file_prefix != current_file {
missing_files.push(path.to_str().unwrap().to_string());

View file

@ -5,6 +5,7 @@
//@normalize-stderr-test: "'rustc'" -> "'<unnamed>'"
//@normalize-stderr-test: "rustc 1\.\d+.* running on .*" -> "rustc <version> running on <target>"
//@normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> ""
//@normalize-stderr-test: "this compiler `.*` is outdated" -> "this compiler <version> is outdated"
#![deny(clippy::internal)]
#![allow(clippy::missing_clippy_version_attribute)]

View file

@ -4,11 +4,14 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml
note: it seems that this compiler <version> is outdated, a newer nightly should have been released in the mean time
|
= note: please consider running `rustup update nightly` to update the nightly channel and check if this problem still persists
= note: if the problem still persists, we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml
note: rustc <version> running on <target>
note: compiler flags: -Z ui-testing
note: compiler flags: -Z ui-testing -Z deduplicate-diagnostics=no
note: Clippy version: foo

View file

@ -10,22 +10,11 @@ use rustc_hir::hir_id::HirId;
use rustc_lint::{Lint, LintContext};
use rustc_middle::ty::TyCtxt;
pub fn a(
cx: impl LintContext,
lint: &'static Lint,
span: impl Into<MultiSpan>,
msg: impl Into<DiagMessage>)
{
pub fn a(cx: impl LintContext, lint: &'static Lint, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
cx.span_lint(lint, span, msg, |_| {});
}
pub fn b(
tcx: TyCtxt<'_>,
lint: &'static Lint,
hir_id: HirId,
span: impl Into<MultiSpan>,
msg: impl Into<DiagMessage>,
) {
pub fn b(tcx: TyCtxt<'_>, lint: &'static Lint, hir_id: HirId, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
tcx.node_span_lint(lint, hir_id, span, msg, |_| {});
}

View file

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

View file

@ -19,8 +19,8 @@ LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]
error: hardcoded path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:43
|
LL | const OPS_MOD: [&str; 5] = ["core", "ops"];
| ^^^^^^^^^^^^^^^
LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: convert all references to use `sym::deref_method`

View file

@ -1,5 +1,5 @@
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:266:19
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:271:19
|
LL | /* Safety: */ unsafe {}
| ^^^^^^^^^
@ -9,7 +9,7 @@ LL | /* Safety: */ unsafe {}
= help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]`
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:270:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:275:5
|
LL | unsafe {}
| ^^^^^^^^^
@ -17,7 +17,7 @@ LL | unsafe {}
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:274:14
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:279:14
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
| ^^^^^^^^^^^^^
@ -25,7 +25,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:274:29
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:279:29
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
| ^^^^^^^^^^^^^
@ -33,7 +33,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:274:48
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:279:48
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
| ^^^^^^^^^^^^^
@ -41,7 +41,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:278:18
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:283:18
|
LL | let _ = (42, unsafe {}, "test", unsafe {});
| ^^^^^^^^^
@ -49,7 +49,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {});
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:278:37
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:283:37
|
LL | let _ = (42, unsafe {}, "test", unsafe {});
| ^^^^^^^^^
@ -57,7 +57,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {});
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:282:14
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:287:14
|
LL | let _ = *unsafe { &42 };
| ^^^^^^^^^^^^^^
@ -65,7 +65,7 @@ LL | let _ = *unsafe { &42 };
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:287:19
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:292:19
|
LL | let _ = match unsafe {} {
| ^^^^^^^^^
@ -73,7 +73,7 @@ LL | let _ = match unsafe {} {
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:293:14
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:298:14
|
LL | let _ = &unsafe {};
| ^^^^^^^^^
@ -81,7 +81,7 @@ LL | let _ = &unsafe {};
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:297:14
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:302:14
|
LL | let _ = [unsafe {}; 5];
| ^^^^^^^^^
@ -89,7 +89,7 @@ LL | let _ = [unsafe {}; 5];
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:301:13
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:306:13
|
LL | let _ = unsafe {};
| ^^^^^^^^^
@ -97,7 +97,7 @@ LL | let _ = unsafe {};
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:311:8
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:316:8
|
LL | t!(unsafe {});
| ^^^^^^^^^
@ -105,7 +105,7 @@ LL | t!(unsafe {});
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:317:13
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:322:13
|
LL | unsafe {}
| ^^^^^^^^^
@ -117,7 +117,7 @@ LL | t!();
= note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:325:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:330:5
|
LL | unsafe {} // SAFETY:
| ^^^^^^^^^
@ -125,7 +125,7 @@ LL | unsafe {} // SAFETY:
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:329:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:334:5
|
LL | unsafe {
| ^^^^^^^^
@ -133,7 +133,7 @@ LL | unsafe {
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:339:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:344:5
|
LL | unsafe {};
| ^^^^^^^^^
@ -141,7 +141,7 @@ LL | unsafe {};
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:343:20
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:348:20
|
LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -149,7 +149,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:350:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:355:5
|
LL | unsafe impl A for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^
@ -157,7 +157,7 @@ LL | unsafe impl A for () {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:357:9
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:362:9
|
LL | unsafe impl B for (u32) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -165,7 +165,7 @@ LL | unsafe impl B for (u32) {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:378:13
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:383:13
|
LL | unsafe impl T for $t {}
| ^^^^^^^^^^^^^^^^^^^^^^^
@ -177,7 +177,7 @@ LL | no_safety_comment!(());
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:403:13
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:408:13
|
LL | unsafe impl T for $t {}
| ^^^^^^^^^^^^^^^^^^^^^^^
@ -189,7 +189,7 @@ LL | no_safety_comment!(());
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:411:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:416:5
|
LL | unsafe impl T for (i32) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -197,7 +197,7 @@ LL | unsafe impl T for (i32) {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:403:13
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:408:13
|
LL | unsafe impl T for $t {}
| ^^^^^^^^^^^^^^^^^^^^^^^
@ -209,7 +209,7 @@ LL | no_safety_comment!(u32);
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:417:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:422:5
|
LL | unsafe impl T for (bool) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -217,7 +217,7 @@ LL | unsafe impl T for (bool) {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:463:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:468:5
|
LL | unsafe impl NoComment for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -225,7 +225,7 @@ LL | unsafe impl NoComment for () {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:467:19
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:472:19
|
LL | /* SAFETY: */ unsafe impl InlineComment for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -233,7 +233,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:471:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:476:5
|
LL | unsafe impl TrailingComment for () {} // SAFETY:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -241,13 +241,13 @@ LL | unsafe impl TrailingComment for () {} // SAFETY:
= help: consider adding a safety comment on the preceding line
error: constant item has unnecessary safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:475:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:480:5
|
LL | const BIG_NUMBER: i32 = 1000000;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: consider removing the safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:474:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:479:5
|
LL | // SAFETY:
| ^^^^^^^^^^
@ -255,7 +255,7 @@ LL | // SAFETY:
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]`
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:476:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:481:5
|
LL | unsafe impl Interference for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -263,7 +263,7 @@ LL | unsafe impl Interference for () {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:483:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:488:5
|
LL | unsafe impl ImplInFn for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -271,7 +271,7 @@ LL | unsafe impl ImplInFn for () {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:492:1
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:497:1
|
LL | unsafe impl CrateRoot for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -279,7 +279,7 @@ LL | unsafe impl CrateRoot for () {}
= help: consider adding a safety comment on the preceding line
error: statement has unnecessary safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:505:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:510:5
|
LL | / let _ = {
LL | | if unsafe { true } {
@ -291,13 +291,13 @@ LL | | };
| |______^
|
help: consider removing the safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:504:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:509:5
|
LL | // SAFETY: this is more than one level away, so it should warn
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:506:12
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:511:12
|
LL | if unsafe { true } {
| ^^^^^^^^^^^^^^^
@ -305,7 +305,7 @@ LL | if unsafe { true } {
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:509:23
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:514:23
|
LL | let bar = unsafe {};
| ^^^^^^^^^

View file

@ -1,5 +1,5 @@
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:266:19
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:271:19
|
LL | /* Safety: */ unsafe {}
| ^^^^^^^^^
@ -9,7 +9,7 @@ LL | /* Safety: */ unsafe {}
= help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]`
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:270:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:275:5
|
LL | unsafe {}
| ^^^^^^^^^
@ -17,7 +17,7 @@ LL | unsafe {}
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:274:14
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:279:14
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
| ^^^^^^^^^^^^^
@ -25,7 +25,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:274:29
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:279:29
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
| ^^^^^^^^^^^^^
@ -33,7 +33,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:274:48
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:279:48
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
| ^^^^^^^^^^^^^
@ -41,7 +41,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:278:18
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:283:18
|
LL | let _ = (42, unsafe {}, "test", unsafe {});
| ^^^^^^^^^
@ -49,7 +49,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {});
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:278:37
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:283:37
|
LL | let _ = (42, unsafe {}, "test", unsafe {});
| ^^^^^^^^^
@ -57,7 +57,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {});
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:282:14
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:287:14
|
LL | let _ = *unsafe { &42 };
| ^^^^^^^^^^^^^^
@ -65,7 +65,7 @@ LL | let _ = *unsafe { &42 };
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:287:19
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:292:19
|
LL | let _ = match unsafe {} {
| ^^^^^^^^^
@ -73,7 +73,7 @@ LL | let _ = match unsafe {} {
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:293:14
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:298:14
|
LL | let _ = &unsafe {};
| ^^^^^^^^^
@ -81,7 +81,7 @@ LL | let _ = &unsafe {};
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:297:14
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:302:14
|
LL | let _ = [unsafe {}; 5];
| ^^^^^^^^^
@ -89,7 +89,7 @@ LL | let _ = [unsafe {}; 5];
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:301:13
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:306:13
|
LL | let _ = unsafe {};
| ^^^^^^^^^
@ -97,7 +97,7 @@ LL | let _ = unsafe {};
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:311:8
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:316:8
|
LL | t!(unsafe {});
| ^^^^^^^^^
@ -105,7 +105,7 @@ LL | t!(unsafe {});
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:317:13
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:322:13
|
LL | unsafe {}
| ^^^^^^^^^
@ -117,7 +117,7 @@ LL | t!();
= note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:325:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:330:5
|
LL | unsafe {} // SAFETY:
| ^^^^^^^^^
@ -125,7 +125,7 @@ LL | unsafe {} // SAFETY:
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:329:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:334:5
|
LL | unsafe {
| ^^^^^^^^
@ -133,7 +133,7 @@ LL | unsafe {
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:339:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:344:5
|
LL | unsafe {};
| ^^^^^^^^^
@ -141,7 +141,7 @@ LL | unsafe {};
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:343:20
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:348:20
|
LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -149,7 +149,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:350:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:355:5
|
LL | unsafe impl A for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^
@ -157,7 +157,7 @@ LL | unsafe impl A for () {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:357:9
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:362:9
|
LL | unsafe impl B for (u32) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -165,7 +165,7 @@ LL | unsafe impl B for (u32) {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:378:13
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:383:13
|
LL | unsafe impl T for $t {}
| ^^^^^^^^^^^^^^^^^^^^^^^
@ -177,7 +177,7 @@ LL | no_safety_comment!(());
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:403:13
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:408:13
|
LL | unsafe impl T for $t {}
| ^^^^^^^^^^^^^^^^^^^^^^^
@ -189,7 +189,7 @@ LL | no_safety_comment!(());
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:411:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:416:5
|
LL | unsafe impl T for (i32) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -197,7 +197,7 @@ LL | unsafe impl T for (i32) {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:403:13
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:408:13
|
LL | unsafe impl T for $t {}
| ^^^^^^^^^^^^^^^^^^^^^^^
@ -209,7 +209,7 @@ LL | no_safety_comment!(u32);
= note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:417:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:422:5
|
LL | unsafe impl T for (bool) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -217,7 +217,7 @@ LL | unsafe impl T for (bool) {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:463:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:468:5
|
LL | unsafe impl NoComment for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -225,7 +225,7 @@ LL | unsafe impl NoComment for () {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:467:19
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:472:19
|
LL | /* SAFETY: */ unsafe impl InlineComment for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -233,7 +233,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:471:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:476:5
|
LL | unsafe impl TrailingComment for () {} // SAFETY:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -241,13 +241,13 @@ LL | unsafe impl TrailingComment for () {} // SAFETY:
= help: consider adding a safety comment on the preceding line
error: constant item has unnecessary safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:475:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:480:5
|
LL | const BIG_NUMBER: i32 = 1000000;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: consider removing the safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:474:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:479:5
|
LL | // SAFETY:
| ^^^^^^^^^^
@ -255,7 +255,7 @@ LL | // SAFETY:
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]`
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:476:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:481:5
|
LL | unsafe impl Interference for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -263,7 +263,7 @@ LL | unsafe impl Interference for () {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:483:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:488:5
|
LL | unsafe impl ImplInFn for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -271,7 +271,7 @@ LL | unsafe impl ImplInFn for () {}
= help: consider adding a safety comment on the preceding line
error: unsafe impl missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:492:1
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:497:1
|
LL | unsafe impl CrateRoot for () {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -279,7 +279,7 @@ LL | unsafe impl CrateRoot for () {}
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:502:9
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:507:9
|
LL | unsafe {};
| ^^^^^^^^^
@ -287,7 +287,7 @@ LL | unsafe {};
= help: consider adding a safety comment on the preceding line
error: statement has unnecessary safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:505:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:510:5
|
LL | / let _ = {
LL | | if unsafe { true } {
@ -299,13 +299,13 @@ LL | | };
| |______^
|
help: consider removing the safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:504:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:509:5
|
LL | // SAFETY: this is more than one level away, so it should warn
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:506:12
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:511:12
|
LL | if unsafe { true } {
| ^^^^^^^^^^^^^^^
@ -313,7 +313,7 @@ LL | if unsafe { true } {
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:509:23
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:514:23
|
LL | let bar = unsafe {};
| ^^^^^^^^^
@ -321,7 +321,7 @@ LL | let bar = unsafe {};
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:527:9
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:532:9
|
LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -329,7 +329,7 @@ LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() };
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:531:9
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:536:9
|
LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -337,7 +337,7 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line()
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:535:9
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:540:9
|
LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -345,7 +345,7 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line()
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:541:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:546:5
|
LL | unsafe {}
| ^^^^^^^^^
@ -353,7 +353,7 @@ LL | unsafe {}
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:545:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:550:5
|
LL | unsafe {
| ^^^^^^^^
@ -361,7 +361,7 @@ LL | unsafe {
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:552:9
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:557:9
|
LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -369,7 +369,7 @@ LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() };
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:557:9
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:562:9
|
LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -377,7 +377,7 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line()
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:563:9
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:568:9
|
LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -385,7 +385,7 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line()
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:568:5
--> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:573:5
|
LL | unsafe {}
| ^^^^^^^^^

View file

@ -4,7 +4,12 @@
//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled
#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)]
#![allow(deref_nullptr, non_local_definitions, clippy::let_unit_value, clippy::missing_safety_doc)]
#![allow(
deref_nullptr,
non_local_definitions,
clippy::let_unit_value,
clippy::missing_safety_doc
)]
#![feature(lint_reasons)]
extern crate proc_macro_unsafe;

View file

@ -0,0 +1,222 @@
#![allow(unused)]
#![allow(clippy::redundant_clone)]
#![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612
#![allow(clippy::needless_late_init)]
#![allow(clippy::box_collection)]
#![warn(clippy::assigning_clones)]
use std::borrow::ToOwned;
use std::ops::{Add, Deref, DerefMut};
// Clone
pub struct HasCloneFrom;
impl Clone for HasCloneFrom {
fn clone(&self) -> Self {
Self
}
fn clone_from(&mut self, source: &Self) {
*self = HasCloneFrom;
}
}
fn clone_method_rhs_val(mut_thing: &mut HasCloneFrom, value_thing: HasCloneFrom) {
mut_thing.clone_from(&value_thing);
}
fn clone_method_rhs_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
mut_thing.clone_from(ref_thing);
}
fn clone_method_lhs_val(mut mut_thing: HasCloneFrom, ref_thing: &HasCloneFrom) {
mut_thing.clone_from(ref_thing);
}
fn clone_function_lhs_mut_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
Clone::clone_from(mut_thing, ref_thing);
}
fn clone_function_lhs_val(mut mut_thing: HasCloneFrom, ref_thing: &HasCloneFrom) {
Clone::clone_from(&mut mut_thing, ref_thing);
}
fn clone_function_through_trait(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
Clone::clone_from(mut_thing, ref_thing);
}
fn clone_function_through_type(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
Clone::clone_from(mut_thing, ref_thing);
}
fn clone_function_fully_qualified(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
Clone::clone_from(mut_thing, ref_thing);
}
fn clone_method_lhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
// These parens should be kept as necessary for a receiver
(mut_thing + &mut HasCloneFrom).clone_from(ref_thing);
}
fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
// These parens should be removed since they are not needed in a function argument
mut_thing.clone_from(ref_thing + ref_thing);
}
fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom {
let mut a = HasCloneFrom;
for _ in 1..10 {
a.clone_from(&b);
}
a
}
fn assign_to_late_init_mut_var(b: HasCloneFrom) {
let mut a;
a = HasCloneFrom;
a = b.clone();
}
fn assign_to_uninit_var(b: HasCloneFrom) {
let a;
a = b.clone();
}
fn assign_to_uninit_mut_var(b: HasCloneFrom) {
let mut a;
a = b.clone();
}
#[derive(Clone)]
pub struct HasDeriveClone;
fn ignore_derive_clone(a: &mut HasDeriveClone, b: &HasDeriveClone) {
// Should not be linted, since the Clone impl is derived
*a = b.clone();
}
pub struct HasCloneImpl;
impl Clone for HasCloneImpl {
fn clone(&self) -> Self {
Self
}
}
fn ignore_missing_clone_from(a: &mut HasCloneImpl, b: &HasCloneImpl) {
// Should not be linted, since the Clone impl doesn't override clone_from
*a = b.clone();
}
struct FakeClone;
impl FakeClone {
/// This looks just like `Clone::clone`
fn clone(&self) -> Self {
FakeClone
}
}
fn ignore_fake_clone() {
let mut a = FakeClone;
let b = FakeClone;
// Should not be linted, since the Clone impl doesn't come from std
a = b.clone();
}
fn ignore_generic_clone<T: Clone>(a: &mut T, b: &T) {
// Should not be linted, since we don't know the actual clone impl
*a = b.clone();
}
macro_rules! clone_inside {
($a:expr, $b: expr) => {
$a = $b.clone();
};
}
fn clone_inside_macro() {
let mut a = String::new();
let b = String::new();
clone_inside!(a, b);
}
// ToOwned
fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) {
ref_str.clone_into(mut_string);
}
fn owned_method_val(mut mut_string: String, ref_str: &str) {
ref_str.clone_into(&mut mut_string);
}
struct HasDeref {
a: String,
}
impl Deref for HasDeref {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.a
}
}
impl DerefMut for HasDeref {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.a
}
}
fn owned_method_box(mut_box_string: &mut Box<String>, ref_str: &str) {
ref_str.clone_into(&mut (*mut_box_string));
}
fn owned_method_deref(mut_box_string: &mut HasDeref, ref_str: &str) {
ref_str.clone_into(&mut (*mut_box_string));
}
fn owned_function_mut_ref(mut_thing: &mut String, ref_str: &str) {
ToOwned::clone_into(ref_str, mut_thing);
}
fn owned_function_val(mut mut_thing: String, ref_str: &str) {
ToOwned::clone_into(ref_str, &mut mut_thing);
}
struct FakeToOwned;
impl FakeToOwned {
/// This looks just like `ToOwned::to_owned`
fn to_owned(&self) -> Self {
FakeToOwned
}
}
fn fake_to_owned() {
let mut a = FakeToOwned;
let b = FakeToOwned;
// Should not be linted, since the ToOwned impl doesn't come from std
a = b.to_owned();
}
fn main() {}
/// Trait implementation to allow producing a `Thing` with a low-precedence expression.
impl Add for HasCloneFrom {
type Output = Self;
fn add(self, _: HasCloneFrom) -> Self {
self
}
}
/// Trait implementation to allow producing a `&Thing` with a low-precedence expression.
impl<'a> Add for &'a HasCloneFrom {
type Output = Self;
fn add(self, _: &'a HasCloneFrom) -> Self {
self
}
}
/// Trait implementation to allow producing a `&mut Thing` with a low-precedence expression.
impl<'a> Add for &'a mut HasCloneFrom {
type Output = Self;
fn add(self, _: &'a mut HasCloneFrom) -> Self {
self
}
}

View file

@ -0,0 +1,222 @@
#![allow(unused)]
#![allow(clippy::redundant_clone)]
#![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612
#![allow(clippy::needless_late_init)]
#![allow(clippy::box_collection)]
#![warn(clippy::assigning_clones)]
use std::borrow::ToOwned;
use std::ops::{Add, Deref, DerefMut};
// Clone
pub struct HasCloneFrom;
impl Clone for HasCloneFrom {
fn clone(&self) -> Self {
Self
}
fn clone_from(&mut self, source: &Self) {
*self = HasCloneFrom;
}
}
fn clone_method_rhs_val(mut_thing: &mut HasCloneFrom, value_thing: HasCloneFrom) {
*mut_thing = value_thing.clone();
}
fn clone_method_rhs_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
*mut_thing = ref_thing.clone();
}
fn clone_method_lhs_val(mut mut_thing: HasCloneFrom, ref_thing: &HasCloneFrom) {
mut_thing = ref_thing.clone();
}
fn clone_function_lhs_mut_ref(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
*mut_thing = Clone::clone(ref_thing);
}
fn clone_function_lhs_val(mut mut_thing: HasCloneFrom, ref_thing: &HasCloneFrom) {
mut_thing = Clone::clone(ref_thing);
}
fn clone_function_through_trait(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
*mut_thing = Clone::clone(ref_thing);
}
fn clone_function_through_type(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
*mut_thing = HasCloneFrom::clone(ref_thing);
}
fn clone_function_fully_qualified(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
*mut_thing = <HasCloneFrom as Clone>::clone(ref_thing);
}
fn clone_method_lhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
// These parens should be kept as necessary for a receiver
*(mut_thing + &mut HasCloneFrom) = ref_thing.clone();
}
fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFrom) {
// These parens should be removed since they are not needed in a function argument
*mut_thing = (ref_thing + ref_thing).clone();
}
fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom {
let mut a = HasCloneFrom;
for _ in 1..10 {
a = b.clone();
}
a
}
fn assign_to_late_init_mut_var(b: HasCloneFrom) {
let mut a;
a = HasCloneFrom;
a = b.clone();
}
fn assign_to_uninit_var(b: HasCloneFrom) {
let a;
a = b.clone();
}
fn assign_to_uninit_mut_var(b: HasCloneFrom) {
let mut a;
a = b.clone();
}
#[derive(Clone)]
pub struct HasDeriveClone;
fn ignore_derive_clone(a: &mut HasDeriveClone, b: &HasDeriveClone) {
// Should not be linted, since the Clone impl is derived
*a = b.clone();
}
pub struct HasCloneImpl;
impl Clone for HasCloneImpl {
fn clone(&self) -> Self {
Self
}
}
fn ignore_missing_clone_from(a: &mut HasCloneImpl, b: &HasCloneImpl) {
// Should not be linted, since the Clone impl doesn't override clone_from
*a = b.clone();
}
struct FakeClone;
impl FakeClone {
/// This looks just like `Clone::clone`
fn clone(&self) -> Self {
FakeClone
}
}
fn ignore_fake_clone() {
let mut a = FakeClone;
let b = FakeClone;
// Should not be linted, since the Clone impl doesn't come from std
a = b.clone();
}
fn ignore_generic_clone<T: Clone>(a: &mut T, b: &T) {
// Should not be linted, since we don't know the actual clone impl
*a = b.clone();
}
macro_rules! clone_inside {
($a:expr, $b: expr) => {
$a = $b.clone();
};
}
fn clone_inside_macro() {
let mut a = String::new();
let b = String::new();
clone_inside!(a, b);
}
// ToOwned
fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) {
*mut_string = ref_str.to_owned();
}
fn owned_method_val(mut mut_string: String, ref_str: &str) {
mut_string = ref_str.to_owned();
}
struct HasDeref {
a: String,
}
impl Deref for HasDeref {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.a
}
}
impl DerefMut for HasDeref {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.a
}
}
fn owned_method_box(mut_box_string: &mut Box<String>, ref_str: &str) {
**mut_box_string = ref_str.to_owned();
}
fn owned_method_deref(mut_box_string: &mut HasDeref, ref_str: &str) {
**mut_box_string = ref_str.to_owned();
}
fn owned_function_mut_ref(mut_thing: &mut String, ref_str: &str) {
*mut_thing = ToOwned::to_owned(ref_str);
}
fn owned_function_val(mut mut_thing: String, ref_str: &str) {
mut_thing = ToOwned::to_owned(ref_str);
}
struct FakeToOwned;
impl FakeToOwned {
/// This looks just like `ToOwned::to_owned`
fn to_owned(&self) -> Self {
FakeToOwned
}
}
fn fake_to_owned() {
let mut a = FakeToOwned;
let b = FakeToOwned;
// Should not be linted, since the ToOwned impl doesn't come from std
a = b.to_owned();
}
fn main() {}
/// Trait implementation to allow producing a `Thing` with a low-precedence expression.
impl Add for HasCloneFrom {
type Output = Self;
fn add(self, _: HasCloneFrom) -> Self {
self
}
}
/// Trait implementation to allow producing a `&Thing` with a low-precedence expression.
impl<'a> Add for &'a HasCloneFrom {
type Output = Self;
fn add(self, _: &'a HasCloneFrom) -> Self {
self
}
}
/// Trait implementation to allow producing a `&mut Thing` with a low-precedence expression.
impl<'a> Add for &'a mut HasCloneFrom {
type Output = Self;
fn add(self, _: &'a mut HasCloneFrom) -> Self {
self
}
}

View file

@ -0,0 +1,107 @@
error: assigning the result of `Clone::clone()` may be inefficient
--> tests/ui/assigning_clones.rs:24:5
|
LL | *mut_thing = value_thing.clone();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(&value_thing)`
|
= note: `-D clippy::assigning-clones` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::assigning_clones)]`
error: assigning the result of `Clone::clone()` may be inefficient
--> tests/ui/assigning_clones.rs:28:5
|
LL | *mut_thing = ref_thing.clone();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing)`
error: assigning the result of `Clone::clone()` may be inefficient
--> tests/ui/assigning_clones.rs:32:5
|
LL | mut_thing = ref_thing.clone();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing)`
error: assigning the result of `Clone::clone()` may be inefficient
--> tests/ui/assigning_clones.rs:36:5
|
LL | *mut_thing = Clone::clone(ref_thing);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)`
error: assigning the result of `Clone::clone()` may be inefficient
--> tests/ui/assigning_clones.rs:40:5
|
LL | mut_thing = Clone::clone(ref_thing);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut mut_thing, ref_thing)`
error: assigning the result of `Clone::clone()` may be inefficient
--> tests/ui/assigning_clones.rs:44:5
|
LL | *mut_thing = Clone::clone(ref_thing);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)`
error: assigning the result of `Clone::clone()` may be inefficient
--> tests/ui/assigning_clones.rs:48:5
|
LL | *mut_thing = HasCloneFrom::clone(ref_thing);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)`
error: assigning the result of `Clone::clone()` may be inefficient
--> tests/ui/assigning_clones.rs:52:5
|
LL | *mut_thing = <HasCloneFrom as Clone>::clone(ref_thing);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)`
error: assigning the result of `Clone::clone()` may be inefficient
--> tests/ui/assigning_clones.rs:57:5
|
LL | *(mut_thing + &mut HasCloneFrom) = ref_thing.clone();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `(mut_thing + &mut HasCloneFrom).clone_from(ref_thing)`
error: assigning the result of `Clone::clone()` may be inefficient
--> tests/ui/assigning_clones.rs:62:5
|
LL | *mut_thing = (ref_thing + ref_thing).clone();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing + ref_thing)`
error: assigning the result of `Clone::clone()` may be inefficient
--> tests/ui/assigning_clones.rs:68:9
|
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
|
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
|
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
|
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
|
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
|
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
|
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

View file

@ -3,6 +3,6 @@ struct Foo(isize, isize, isize, isize);
pub fn main() {
let Self::anything_here_kills_it(a, b, ..) = Foo(5, 5, 5, 5);
match [5, 5, 5, 5] {
[..] => { }
[..] => {},
}
}

View file

@ -1,7 +1,6 @@
#![allow(clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, dead_code)]
#![warn(clippy::expl_impl_clone_on_copy)]
#[derive(Copy)]
struct Qux;

View file

@ -1,5 +1,5 @@
error: you are implementing `Clone` explicitly on a `Copy` type
--> tests/ui/derive.rs:8:1
--> tests/ui/derive.rs:7:1
|
LL | / impl Clone for Qux {
LL | |
@ -10,7 +10,7 @@ LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
--> tests/ui/derive.rs:8:1
--> tests/ui/derive.rs:7:1
|
LL | / impl Clone for Qux {
LL | |
@ -23,7 +23,7 @@ LL | | }
= help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]`
error: you are implementing `Clone` explicitly on a `Copy` type
--> tests/ui/derive.rs:33:1
--> tests/ui/derive.rs:32:1
|
LL | / impl<'a> Clone for Lt<'a> {
LL | |
@ -34,7 +34,7 @@ LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
--> tests/ui/derive.rs:33:1
--> tests/ui/derive.rs:32:1
|
LL | / impl<'a> Clone for Lt<'a> {
LL | |
@ -45,7 +45,7 @@ LL | | }
| |_^
error: you are implementing `Clone` explicitly on a `Copy` type
--> tests/ui/derive.rs:45:1
--> tests/ui/derive.rs:44:1
|
LL | / impl Clone for BigArray {
LL | |
@ -56,7 +56,7 @@ LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
--> tests/ui/derive.rs:45:1
--> tests/ui/derive.rs:44:1
|
LL | / impl Clone for BigArray {
LL | |
@ -67,7 +67,7 @@ LL | | }
| |_^
error: you are implementing `Clone` explicitly on a `Copy` type
--> tests/ui/derive.rs:57:1
--> tests/ui/derive.rs:56:1
|
LL | / impl Clone for FnPtr {
LL | |
@ -78,7 +78,7 @@ LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
--> tests/ui/derive.rs:57:1
--> tests/ui/derive.rs:56:1
|
LL | / impl Clone for FnPtr {
LL | |
@ -89,7 +89,7 @@ LL | | }
| |_^
error: you are implementing `Clone` explicitly on a `Copy` type
--> tests/ui/derive.rs:78:1
--> tests/ui/derive.rs:77:1
|
LL | / impl<T: Clone> Clone for Generic2<T> {
LL | |
@ -100,7 +100,7 @@ LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
--> tests/ui/derive.rs:78:1
--> tests/ui/derive.rs:77:1
|
LL | / impl<T: Clone> Clone for Generic2<T> {
LL | |

View file

@ -153,4 +153,30 @@ pub enum MissingEqNonExhaustive3 {
Bar,
}
mod struct_gen {
// issue 9413
pub trait Group {
type Element: Eq + PartialEq;
}
pub trait Suite {
type Group: Group;
}
#[derive(PartialEq, Eq)]
//~^ ERROR: you are deriving `PartialEq` and can implement `Eq`
pub struct Foo<C: Suite>(<C::Group as Group>::Element);
#[derive(PartialEq, Eq)]
pub struct Bar<C: Suite>(i32, <C::Group as Group>::Element);
// issue 9319
#[derive(PartialEq, Eq)]
//~^ ERROR: you are deriving `PartialEq` and can implement `Eq`
pub struct Oof<T: Fn()>(T);
#[derive(PartialEq, Eq)]
pub struct Rab<T: Fn()>(T);
}
fn main() {}

View file

@ -153,4 +153,30 @@ pub enum MissingEqNonExhaustive3 {
Bar,
}
mod struct_gen {
// issue 9413
pub trait Group {
type Element: Eq + PartialEq;
}
pub trait Suite {
type Group: Group;
}
#[derive(PartialEq)]
//~^ ERROR: you are deriving `PartialEq` and can implement `Eq`
pub struct Foo<C: Suite>(<C::Group as Group>::Element);
#[derive(PartialEq, Eq)]
pub struct Bar<C: Suite>(i32, <C::Group as Group>::Element);
// issue 9319
#[derive(PartialEq)]
//~^ ERROR: you are deriving `PartialEq` and can implement `Eq`
pub struct Oof<T: Fn()>(T);
#[derive(PartialEq, Eq)]
pub struct Rab<T: Fn()>(T);
}
fn main() {}

View file

@ -67,5 +67,17 @@ error: you are deriving `PartialEq` and can implement `Eq`
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: aborting due to 11 previous errors
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:166:14
|
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:174:14
|
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: aborting due to 13 previous errors

View file

@ -230,3 +230,8 @@ fn issue_11568() {}
/// There is no try (`do()` or `do_not()`).
fn parenthesized_word() {}
/// `ABes`
/// OSes
/// UXes
fn plural_acronym_test() {}

View file

@ -230,3 +230,8 @@ fn issue_11568() {}
/// There is no try (do() or do_not()).
fn parenthesized_word() {}
/// ABes
/// OSes
/// UXes
fn plural_acronym_test() {}

View file

@ -341,5 +341,16 @@ help: try
LL | /// There is no try (do() or `do_not()`).
| ~~~~~~~~~~
error: aborting due to 31 previous errors
error: item in documentation is missing backticks
--> tests/ui/doc/doc-fixable.rs:234:5
|
LL | /// ABes
| ^^^^
|
help: try
|
LL | /// `ABes`
| ~~~~~~
error: aborting due to 32 previous errors

View file

@ -0,0 +1,9 @@
#![warn(clippy::doc_markdown)]
// Should not warn!
/// Blah blah blah <code>[FooBar]&lt;[FooBar]&gt;</code>.
pub struct Foo(u32);
// Should warn.
/// Blah blah blah <code>[FooBar]&lt;[FooBar]&gt;</code>[`FooBar`].
pub struct FooBar(u32);

View file

@ -0,0 +1,9 @@
#![warn(clippy::doc_markdown)]
// Should not warn!
/// Blah blah blah <code>[FooBar]&lt;[FooBar]&gt;</code>.
pub struct Foo(u32);
// Should warn.
/// Blah blah blah <code>[FooBar]&lt;[FooBar]&gt;</code>[FooBar].
pub struct FooBar(u32);

View file

@ -0,0 +1,15 @@
error: item in documentation is missing backticks
--> tests/ui/doc/issue_9473.rs:8:58
|
LL | /// Blah blah blah <code>[FooBar]&lt;[FooBar]&gt;</code>[FooBar].
| ^^^^^^
|
= note: `-D clippy::doc-markdown` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]`
help: try
|
LL | /// Blah blah blah <code>[FooBar]&lt;[FooBar]&gt;</code>[`FooBar`].
| ~~~~~~~~
error: aborting due to 1 previous error

View file

@ -1,3 +1,5 @@
//@compile-flags: -Zdeduplicate-diagnostics=yes
#![warn(clippy::all)]
#![warn(clippy::else_if_without_else)]

View file

@ -1,5 +1,5 @@
error: `if` expression with an `else if`, but without a final `else`
--> tests/ui/else_if_without_else.rs:45:12
--> tests/ui/else_if_without_else.rs:47:12
|
LL | } else if bla2() {
| ____________^
@ -13,7 +13,7 @@ LL | | }
= help: to override `-D warnings` add `#[allow(clippy::else_if_without_else)]`
error: `if` expression with an `else if`, but without a final `else`
--> tests/ui/else_if_without_else.rs:54:12
--> tests/ui/else_if_without_else.rs:56:12
|
LL | } else if bla3() {
| ____________^

View file

@ -1,5 +1,7 @@
#![allow(unused)]
#![warn(clippy::empty_docs)]
#![allow(clippy::mixed_attributes_style)]
mod outer {
//!

View file

@ -1,5 +1,5 @@
error: empty doc comment
--> tests/ui/empty_docs.rs:4:5
--> tests/ui/empty_docs.rs:6:5
|
LL | //!
| ^^^
@ -9,7 +9,7 @@ LL | //!
= help: to override `-D warnings` add `#[allow(clippy::empty_docs)]`
error: empty doc comment
--> tests/ui/empty_docs.rs:12:5
--> tests/ui/empty_docs.rs:14:5
|
LL | ///
| ^^^
@ -17,7 +17,7 @@ LL | ///
= help: consider removing or filling it
error: empty doc comment
--> tests/ui/empty_docs.rs:14:9
--> tests/ui/empty_docs.rs:16:9
|
LL | ///
| ^^^
@ -25,7 +25,7 @@ LL | ///
= help: consider removing or filling it
error: empty doc comment
--> tests/ui/empty_docs.rs:25:5
--> tests/ui/empty_docs.rs:27:5
|
LL | #[doc = ""]
| ^^^^^^^^^^^
@ -33,7 +33,7 @@ LL | #[doc = ""]
= help: consider removing or filling it
error: empty doc comment
--> tests/ui/empty_docs.rs:28:5
--> tests/ui/empty_docs.rs:30:5
|
LL | / #[doc = ""]
LL | | #[doc = ""]
@ -42,7 +42,7 @@ LL | | #[doc = ""]
= help: consider removing or filling it
error: empty doc comment
--> tests/ui/empty_docs.rs:35:5
--> tests/ui/empty_docs.rs:37:5
|
LL | ///
| ^^^
@ -50,7 +50,7 @@ LL | ///
= help: consider removing or filling it
error: empty doc comment
--> tests/ui/empty_docs.rs:48:13
--> tests/ui/empty_docs.rs:50:13
|
LL | /*! */
| ^^^^^^
@ -58,7 +58,7 @@ LL | /*! */
= help: consider removing or filling it
error: empty doc comment
--> tests/ui/empty_docs.rs:56:13
--> tests/ui/empty_docs.rs:58:13
|
LL | ///
| ^^^
@ -66,7 +66,7 @@ LL | ///
= help: consider removing or filling it
error: empty doc comment
--> tests/ui/empty_docs.rs:64:9
--> tests/ui/empty_docs.rs:66:9
|
LL | ///
| ^^^

View file

@ -165,4 +165,15 @@ pub fn issue_10331() {
}
}
/// Issue 11935
/// Do not suggest using entries if the map is used inside the `insert` expression.
pub fn issue_11935() {
let mut counts: HashMap<u64, u64> = HashMap::new();
if !counts.contains_key(&1) {
counts.insert(1, 1);
} else {
counts.insert(1, counts.get(&1).unwrap() + 1);
}
}
fn main() {}

View file

@ -169,4 +169,15 @@ pub fn issue_10331() {
}
}
/// Issue 11935
/// Do not suggest using entries if the map is used inside the `insert` expression.
pub fn issue_11935() {
let mut counts: HashMap<u64, u64> = HashMap::new();
if !counts.contains_key(&1) {
counts.insert(1, 1);
} else {
counts.insert(1, counts.get(&1).unwrap() + 1);
}
}
fn main() {}

View file

@ -6,7 +6,7 @@
clippy::deref_addrof,
clippy::unnecessary_mut_passed,
dead_code,
non_local_definitions,
non_local_definitions
)]
use core::slice;

View file

@ -6,7 +6,7 @@
clippy::deref_addrof,
clippy::unnecessary_mut_passed,
dead_code,
non_local_definitions,
non_local_definitions
)]
use core::slice;

View file

@ -2,6 +2,7 @@
//@aux-build:proc_macros.rs
#![warn(clippy::field_reassign_with_default)]
#![allow(clippy::assigning_clones)]
#[macro_use]
extern crate proc_macro_derive;

View file

@ -1,11 +1,11 @@
error: field assignment outside of initializer for an instance created with Default::default()
--> tests/ui/field_reassign_with_default.rs:56:5
--> tests/ui/field_reassign_with_default.rs:57:5
|
LL | a.i = 42;
| ^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
--> tests/ui/field_reassign_with_default.rs:55:5
--> tests/ui/field_reassign_with_default.rs:56:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -13,121 +13,121 @@ LL | let mut a: A = Default::default();
= help: to override `-D warnings` add `#[allow(clippy::field_reassign_with_default)]`
error: field assignment outside of initializer for an instance created with Default::default()
--> tests/ui/field_reassign_with_default.rs:96:5
--> tests/ui/field_reassign_with_default.rs:97:5
|
LL | a.j = 43;
| ^^^^^^^^^
|
note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments
--> tests/ui/field_reassign_with_default.rs:95:5
--> tests/ui/field_reassign_with_default.rs:96:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> tests/ui/field_reassign_with_default.rs:101:5
--> tests/ui/field_reassign_with_default.rs:102:5
|
LL | a.i = 42;
| ^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments
--> tests/ui/field_reassign_with_default.rs:100:5
--> tests/ui/field_reassign_with_default.rs:101:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> tests/ui/field_reassign_with_default.rs:107:5
--> tests/ui/field_reassign_with_default.rs:108:5
|
LL | a.i = 42;
| ^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
--> tests/ui/field_reassign_with_default.rs:106:5
--> tests/ui/field_reassign_with_default.rs:107:5
|
LL | let mut a = A::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> tests/ui/field_reassign_with_default.rs:117:5
--> tests/ui/field_reassign_with_default.rs:118:5
|
LL | a.i = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments
--> tests/ui/field_reassign_with_default.rs:116:5
--> tests/ui/field_reassign_with_default.rs:117:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> tests/ui/field_reassign_with_default.rs:121:5
--> tests/ui/field_reassign_with_default.rs:122:5
|
LL | a.i = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments
--> tests/ui/field_reassign_with_default.rs:120:5
--> tests/ui/field_reassign_with_default.rs:121:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> tests/ui/field_reassign_with_default.rs:143:5
--> tests/ui/field_reassign_with_default.rs:144:5
|
LL | a.i = vec![1];
| ^^^^^^^^^^^^^^
|
note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments
--> tests/ui/field_reassign_with_default.rs:142:5
--> tests/ui/field_reassign_with_default.rs:143:5
|
LL | let mut a: C = C::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> tests/ui/field_reassign_with_default.rs:161:5
--> tests/ui/field_reassign_with_default.rs:162:5
|
LL | a.i = true;
| ^^^^^^^^^^^
|
note: consider initializing the variable with `Wrapper::<bool> { i: true }` and removing relevant reassignments
--> tests/ui/field_reassign_with_default.rs:160:5
--> tests/ui/field_reassign_with_default.rs:161:5
|
LL | let mut a: Wrapper<bool> = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> tests/ui/field_reassign_with_default.rs:164:5
--> tests/ui/field_reassign_with_default.rs:165:5
|
LL | a.i = 42;
| ^^^^^^^^^
|
note: consider initializing the variable with `WrapperMulti::<i32, i64> { i: 42, ..Default::default() }` and removing relevant reassignments
--> tests/ui/field_reassign_with_default.rs:163:5
--> tests/ui/field_reassign_with_default.rs:164:5
|
LL | let mut a: WrapperMulti<i32, i64> = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> tests/ui/field_reassign_with_default.rs:235:13
--> tests/ui/field_reassign_with_default.rs:236:13
|
LL | f.name = name.len();
| ^^^^^^^^^^^^^^^^^^^^
|
note: consider initializing the variable with `issue6312::ImplDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments
--> tests/ui/field_reassign_with_default.rs:234:13
--> tests/ui/field_reassign_with_default.rs:235:13
|
LL | let mut f = ImplDropAllCopy::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> tests/ui/field_reassign_with_default.rs:251:13
--> tests/ui/field_reassign_with_default.rs:252:13
|
LL | f.name = name.len();
| ^^^^^^^^^^^^^^^^^^^^
|
note: consider initializing the variable with `issue6312::NoDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments
--> tests/ui/field_reassign_with_default.rs:250:13
--> tests/ui/field_reassign_with_default.rs:251:13
|
LL | let mut f = NoDropAllCopy::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -134,7 +134,7 @@ fn main() {
//~^ ERROR: this operation has no effect
f(if b { 1 } else { 2 } + 3);
//~^ ERROR: this operation has no effect
const _: i32 = { 2 * 4 } + 3;
//~^ ERROR: this operation has no effect
const _: i32 = { 1 + 2 * 3 } + 3;

View file

@ -134,7 +134,7 @@ fn main() {
//~^ ERROR: this operation has no effect
f(0 + if b { 1 } else { 2 } + 3);
//~^ ERROR: this operation has no effect
const _: i32 = { 2 * 4 } + 0 + 3;
//~^ ERROR: this operation has no effect
const _: i32 = 0 + { 1 + 2 * 3 } + 3;

View file

@ -1,3 +1,5 @@
//@compile-flags: -Zdeduplicate-diagnostics=yes
#![feature(inline_const)]
#![warn(clippy::indexing_slicing)]
// We also check the out_of_bounds_indexing lint here, because it lints similar things and

View file

@ -1,5 +1,5 @@
error: indexing may panic
--> tests/ui/indexing_slicing_index.rs:14:20
--> tests/ui/indexing_slicing_index.rs:16:20
|
LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
| ^^^^^^^^^^
@ -10,19 +10,19 @@ LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-re
= help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]`
error[E0080]: evaluation of `main::{constant#3}` failed
--> tests/ui/indexing_slicing_index.rs:46:14
--> tests/ui/indexing_slicing_index.rs:48:14
|
LL | const { &ARR[idx4()] };
| ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
note: erroneous constant encountered
--> tests/ui/indexing_slicing_index.rs:46:5
--> tests/ui/indexing_slicing_index.rs:48:5
|
LL | const { &ARR[idx4()] };
| ^^^^^^^^^^^^^^^^^^^^^^
error: indexing may panic
--> tests/ui/indexing_slicing_index.rs:27:5
--> tests/ui/indexing_slicing_index.rs:29:5
|
LL | x[index];
| ^^^^^^^^
@ -30,7 +30,7 @@ LL | x[index];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: index is out of bounds
--> tests/ui/indexing_slicing_index.rs:30:5
--> tests/ui/indexing_slicing_index.rs:32:5
|
LL | x[4];
| ^^^^
@ -39,13 +39,13 @@ LL | x[4];
= help: to override `-D warnings` add `#[allow(clippy::out_of_bounds_indexing)]`
error: index is out of bounds
--> tests/ui/indexing_slicing_index.rs:32:5
--> tests/ui/indexing_slicing_index.rs:34:5
|
LL | x[1 << 3];
| ^^^^^^^^^
error: indexing may panic
--> tests/ui/indexing_slicing_index.rs:43:14
--> tests/ui/indexing_slicing_index.rs:45:14
|
LL | const { &ARR[idx()] };
| ^^^^^^^^^^
@ -54,7 +54,7 @@ LL | const { &ARR[idx()] };
= note: the suggestion might not be applicable in constant blocks
error: indexing may panic
--> tests/ui/indexing_slicing_index.rs:46:14
--> tests/ui/indexing_slicing_index.rs:48:14
|
LL | const { &ARR[idx4()] };
| ^^^^^^^^^^^
@ -63,13 +63,13 @@ LL | const { &ARR[idx4()] };
= note: the suggestion might not be applicable in constant blocks
error: index is out of bounds
--> tests/ui/indexing_slicing_index.rs:53:5
--> tests/ui/indexing_slicing_index.rs:55:5
|
LL | y[4];
| ^^^^
error: indexing may panic
--> tests/ui/indexing_slicing_index.rs:56:5
--> tests/ui/indexing_slicing_index.rs:58:5
|
LL | v[0];
| ^^^^
@ -77,7 +77,7 @@ LL | v[0];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
--> tests/ui/indexing_slicing_index.rs:58:5
--> tests/ui/indexing_slicing_index.rs:60:5
|
LL | v[10];
| ^^^^^
@ -85,7 +85,7 @@ LL | v[10];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
--> tests/ui/indexing_slicing_index.rs:60:5
--> tests/ui/indexing_slicing_index.rs:62:5
|
LL | v[1 << 3];
| ^^^^^^^^^
@ -93,13 +93,13 @@ LL | v[1 << 3];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: index is out of bounds
--> tests/ui/indexing_slicing_index.rs:68:5
--> tests/ui/indexing_slicing_index.rs:70:5
|
LL | x[N];
| ^^^^
error: indexing may panic
--> tests/ui/indexing_slicing_index.rs:71:5
--> tests/ui/indexing_slicing_index.rs:73:5
|
LL | v[N];
| ^^^^
@ -107,7 +107,7 @@ LL | v[N];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
--> tests/ui/indexing_slicing_index.rs:73:5
--> tests/ui/indexing_slicing_index.rs:75:5
|
LL | v[M];
| ^^^^
@ -115,7 +115,7 @@ LL | v[M];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: index is out of bounds
--> tests/ui/indexing_slicing_index.rs:77:13
--> tests/ui/indexing_slicing_index.rs:79:13
|
LL | let _ = x[4];
| ^^^^

View file

@ -73,3 +73,5 @@ fn main() {
#[allow(clippy::let_underscore_untyped)]
let _ = a();
}
async fn dont_lint_async_prototype(_: u8) {}

View file

@ -1,3 +1,4 @@
#![feature(try_blocks)]
#![allow(unused_braces, unused_variables, dead_code)]
#![allow(
clippy::collapsible_else_if,
@ -446,3 +447,12 @@ struct U<T> {
w: T,
x: T,
}
fn issue12337() {
// We want to generally silence question_mark lints within try blocks, since `?` has different
// behavior to `return`, and question_mark calls into manual_let_else logic, so make sure that
// we still emit a lint for manual_let_else
let _: Option<()> = try {
let v = if let Some(v_some) = g() { v_some } else { return };
};
}

View file

@ -1,5 +1,5 @@
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:27:5
--> tests/ui/manual_let_else.rs:28:5
|
LL | let v = if let Some(v_some) = g() { v_some } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };`
@ -8,7 +8,7 @@ LL | let v = if let Some(v_some) = g() { v_some } else { return };
= help: to override `-D warnings` add `#[allow(clippy::manual_let_else)]`
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:30:5
--> tests/ui/manual_let_else.rs:31:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@ -26,7 +26,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:37:5
--> tests/ui/manual_let_else.rs:38:5
|
LL | / let v = if let Some(v) = g() {
LL | |
@ -47,25 +47,25 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:49:9
--> tests/ui/manual_let_else.rs:50:9
|
LL | let v = if let Some(v_some) = g() { v_some } else { continue };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { continue };`
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:51:9
--> tests/ui/manual_let_else.rs:52:9
|
LL | let v = if let Some(v_some) = g() { v_some } else { break };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { break };`
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:56:5
--> tests/ui/manual_let_else.rs:57:5
|
LL | let v = if let Some(v_some) = g() { v_some } else { panic!() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { panic!() };`
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:60:5
--> tests/ui/manual_let_else.rs:61:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@ -83,7 +83,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:68:5
--> tests/ui/manual_let_else.rs:69:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@ -101,7 +101,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:76:5
--> tests/ui/manual_let_else.rs:77:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@ -121,7 +121,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:85:5
--> tests/ui/manual_let_else.rs:86:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@ -141,7 +141,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:94:5
--> tests/ui/manual_let_else.rs:95:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@ -168,7 +168,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:110:5
--> tests/ui/manual_let_else.rs:111:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@ -190,7 +190,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:121:5
--> tests/ui/manual_let_else.rs:122:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@ -217,7 +217,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:137:5
--> tests/ui/manual_let_else.rs:138:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@ -239,7 +239,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:148:5
--> tests/ui/manual_let_else.rs:149:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@ -257,7 +257,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:156:5
--> tests/ui/manual_let_else.rs:157:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@ -278,7 +278,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:166:5
--> tests/ui/manual_let_else.rs:167:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@ -299,7 +299,7 @@ LL + } };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:176:5
--> tests/ui/manual_let_else.rs:177:5
|
LL | / let v = if let Some(v_some) = g() {
LL | |
@ -328,7 +328,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:194:5
--> tests/ui/manual_let_else.rs:195:5
|
LL | / let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) {
LL | |
@ -346,7 +346,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:202:5
--> tests/ui/manual_let_else.rs:203:5
|
LL | / let (w, S { v }) = if let (Some(v_some), w_some) = (g().map(|_| S { v: 0 }), 0) {
LL | |
@ -364,7 +364,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:212:13
--> tests/ui/manual_let_else.rs:213:13
|
LL | let $n = if let Some(v) = $e { v } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some($n) = g() else { return };`
@ -375,19 +375,19 @@ LL | create_binding_if_some!(w, g());
= note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info)
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:221:5
--> tests/ui/manual_let_else.rs:222:5
|
LL | let v = if let Variant::A(a, 0) = e() { a } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(v, 0) = e() else { return };`
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:225:5
--> tests/ui/manual_let_else.rs:226:5
|
LL | let mut v = if let Variant::B(b) = e() { b } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(mut v) = e() else { return };`
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:230:5
--> tests/ui/manual_let_else.rs:231:5
|
LL | / let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested {
LL | |
@ -405,19 +405,19 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:237:5
--> tests/ui/manual_let_else.rs:238:5
|
LL | let v = if let Variant::A(.., a) = e() { a } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(.., v) = e() else { return };`
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:241:5
--> tests/ui/manual_let_else.rs:242:5
|
LL | let w = if let (Some(v), ()) = (g(), ()) { v } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let (Some(w), ()) = (g(), ()) else { return };`
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:245:5
--> tests/ui/manual_let_else.rs:246:5
|
LL | / let w = if let Some(S { v: x }) = Some(S { v: 0 }) {
LL | |
@ -435,7 +435,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:253:5
--> tests/ui/manual_let_else.rs:254:5
|
LL | / let v = if let Some(S { v: x }) = Some(S { v: 0 }) {
LL | |
@ -453,7 +453,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:261:5
--> tests/ui/manual_let_else.rs:262:5
|
LL | / let (x, S { v }, w) = if let Some(U { v, w, x }) = None::<U<S<()>>> {
LL | |
@ -471,7 +471,7 @@ LL + };
|
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:378:5
--> tests/ui/manual_let_else.rs:379:5
|
LL | / let _ = match ff {
LL | |
@ -480,5 +480,11 @@ LL | | _ => macro_call!(),
LL | | };
| |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };`
error: aborting due to 30 previous errors
error: this could be rewritten as `let...else`
--> tests/ui/manual_let_else.rs:456:9
|
LL | let v = if let Some(v_some) = g() { v_some } else { return };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };`
error: aborting due to 31 previous errors

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