Auto merge of #116489 - flip1995:clippyup, r=Manishearth

Clippy subtree sync

r? `@Manishearth`
This commit is contained in:
bors 2023-10-06 16:46:13 +00:00
commit 31be8cc411
109 changed files with 2697 additions and 804 deletions

View File

@ -508,10 +508,12 @@ checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
[[package]]
name = "clippy"
version = "0.1.74"
version = "0.1.75"
dependencies = [
"anstream",
"clippy_lints",
"clippy_utils",
"color-print",
"filetime",
"futures",
"if_chain",
@ -546,7 +548,7 @@ dependencies = [
[[package]]
name = "clippy_lints"
version = "0.1.74"
version = "0.1.75"
dependencies = [
"arrayvec",
"cargo_metadata",
@ -566,11 +568,12 @@ dependencies = [
"unicode-normalization",
"unicode-script",
"url",
"walkdir",
]
[[package]]
name = "clippy_utils"
version = "0.1.74"
version = "0.1.75"
dependencies = [
"arrayvec",
"if_chain",
@ -603,6 +606,27 @@ dependencies = [
"tracing-error",
]
[[package]]
name = "color-print"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a858372ff14bab9b1b30ea504f2a4bc534582aee3e42ba2d41d2a7baba63d5d"
dependencies = [
"color-print-proc-macro",
]
[[package]]
name = "color-print-proc-macro"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57e37866456a721d0a404439a1adae37a31be4e0055590d053dfe6981e05003f"
dependencies = [
"nom",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "color-spantrace"
version = "0.2.0"
@ -933,7 +957,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69"
[[package]]
name = "declare_clippy_lint"
version = "0.1.74"
version = "0.1.75"
dependencies = [
"itertools",
"quote",

View File

@ -6,11 +6,101 @@ document.
## Unreleased / Beta / In Rust Nightly
[37f4c172...master](https://github.com/rust-lang/rust-clippy/compare/37f4c172...master)
[1e8fdf49...master](https://github.com/rust-lang/rust-clippy/compare/1e8fdf49...master)
## Rust 1.73
Current stable, released 2023-10-05
[View all 103 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-07-02T12%3A24%3A40Z..2023-08-11T11%3A09%3A56Z+base%3Amaster)
### New Lints
* [`impossible_comparisons`]
[#10843](https://github.com/rust-lang/rust-clippy/pull/10843)
* [`redundant_comparisons`]
[#10843](https://github.com/rust-lang/rust-clippy/pull/10843)
* [`ignored_unit_patterns`]
[#11242](https://github.com/rust-lang/rust-clippy/pull/11242)
* [`readonly_write_lock`]
[#11210](https://github.com/rust-lang/rust-clippy/pull/11210)
* [`filter_map_bool_then`]
[#11115](https://github.com/rust-lang/rust-clippy/pull/11115)
* [`needless_return_with_question_mark`]
[#11031](https://github.com/rust-lang/rust-clippy/pull/11031)
* [`redundant_guards`]
[#10955](https://github.com/rust-lang/rust-clippy/pull/10955)
* [`redundant_locals`]
[#10885](https://github.com/rust-lang/rust-clippy/pull/10885)
* [`absolute_paths`]
[#11003](https://github.com/rust-lang/rust-clippy/pull/11003)
* [`error_impl_error`]
[#11107](https://github.com/rust-lang/rust-clippy/pull/11107)
* [`iter_skip_zero`]
[#11046](https://github.com/rust-lang/rust-clippy/pull/11046)
* [`string_lit_chars_any`]
[#11052](https://github.com/rust-lang/rust-clippy/pull/11052)
* [`four_forward_slashes`]
[#11140](https://github.com/rust-lang/rust-clippy/pull/11140)
* [`format_collect`]
[#11116](https://github.com/rust-lang/rust-clippy/pull/11116)
* [`needless_pass_by_ref_mut`]
[#10900](https://github.com/rust-lang/rust-clippy/pull/10900)
* [`manual_is_infinite`]
[#11049](https://github.com/rust-lang/rust-clippy/pull/11049)
* [`manual_is_finite`]
[#11049](https://github.com/rust-lang/rust-clippy/pull/11049)
* [`incorrect_partial_ord_impl_on_ord_type`]
[#10788](https://github.com/rust-lang/rust-clippy/pull/10788)
* [`read_line_without_trim`]
[#10970](https://github.com/rust-lang/rust-clippy/pull/10970)
* [`type_id_on_box`]
[#10987](https://github.com/rust-lang/rust-clippy/pull/10987)
### Moves and Deprecations
* Renamed `unwrap_or_else_default` to [`unwrap_or_default`]
[#10120](https://github.com/rust-lang/rust-clippy/pull/10120)
* Moved [`tuple_array_conversions`] to `pedantic` (Now allow-by-default)
[#11146](https://github.com/rust-lang/rust-clippy/pull/11146)
* Moved [`arc_with_non_send_sync`] to `suspicious` (Now warn-by-default)
[#11104](https://github.com/rust-lang/rust-clippy/pull/11104)
* Moved [`needless_raw_string_hashes`] to `pedantic` (Now allow-by-default)
[#11415](https://github.com/rust-lang/rust-clippy/pull/11415)
### Enhancements
* [`unwrap_used`]: No longer lints on the never-type or never-like enums
[#11252](https://github.com/rust-lang/rust-clippy/pull/11252)
* [`expect_used`]: No longer lints on the never-type or never-like enums
[#11252](https://github.com/rust-lang/rust-clippy/pull/11252)
### False Positive Fixes
* [`panic_in_result_fn`]: No longer triggers on `todo!`, `unimplemented!`, `unreachable!`
[#11123](https://github.com/rust-lang/rust-clippy/pull/11123)
### Suggestion Fixes/Improvements
* [`semicolon_if_nothing_returned`]: The suggestion is now machine-applicable with rustfix
[#11083](https://github.com/rust-lang/rust-clippy/pull/11083)
### ICE Fixes
* [`filter_map_bool_then`]: No longer crashes on late-bound regions
[#11318](https://github.com/rust-lang/rust-clippy/pull/11318)
* [`unwrap_or_default`]: No longer crashes on alias types for local items
[#11258](https://github.com/rust-lang/rust-clippy/pull/11258)
* [`unnecessary_literal_unwrap`]: No longer crashes on `None.unwrap_or_default()`
[#11106](https://github.com/rust-lang/rust-clippy/pull/11106)
* Fixed MIR-related ICE
[#11130](https://github.com/rust-lang/rust-clippy/pull/11130)
* [`missing_fields_in_debug`]: No longer crashes on non-ADT self types
[#11069](https://github.com/rust-lang/rust-clippy/pull/11069)
## Rust 1.72
Current stable, released 2023-08-24
Released 2023-08-24
[View all 131 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-05-22T14%3A53%3A59Z..2023-07-01T22%3A57%3A20Z+base%3Amaster)
@ -5011,6 +5101,7 @@ Released 2018-09-13
[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
[`into_iter_without_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_without_iter
[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
@ -5036,6 +5127,7 @@ Released 2018-09-13
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
[`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero
[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
[`iter_without_into_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_without_into_iter
[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
@ -5072,6 +5164,7 @@ Released 2018-09-13
[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
[`manual_hash_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
[`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite

View File

@ -1,6 +1,6 @@
[package]
name = "clippy"
version = "0.1.74"
version = "0.1.75"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
@ -25,6 +25,8 @@ clippy_lints = { path = "clippy_lints" }
rustc_tools_util = "0.3.0"
tempfile = { version = "3.2", optional = true }
termize = "0.1"
color-print = "0.3.4" # Sync version with Cargo
anstream = "0.5.0"
[dev-dependencies]
ui_test = "0.20"

View File

@ -261,7 +261,7 @@ impl EarlyLintPass for FooFunctions {}
[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
[example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
[category_level_mapping]: ../index.html
## Lint registration

View File

@ -151,6 +151,7 @@ The minimum rust version that the project supports
* [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds)
* [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions)
* [`manual_try_fold`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold)
* [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one)
## `cognitive-complexity-threshold`

View File

@ -58,7 +58,7 @@ pub fn create(
};
create_lint(&lint, msrv).context("Unable to create lint implementation")?;
create_test(&lint).context("Unable to create a test for the new lint")?;
create_test(&lint, msrv).context("Unable to create a test for the new lint")?;
if lint.ty.is_none() {
add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?;
@ -88,15 +88,21 @@ fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
}
}
fn create_test(lint: &LintData<'_>) -> io::Result<()> {
fn create_project_layout<P: Into<PathBuf>>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> {
fn create_test(lint: &LintData<'_>, msrv: bool) -> io::Result<()> {
fn create_project_layout<P: Into<PathBuf>>(
lint_name: &str,
location: P,
case: &str,
hint: &str,
msrv: bool,
) -> io::Result<()> {
let mut path = location.into().join(case);
fs::create_dir(&path)?;
write_file(path.join("Cargo.toml"), get_manifest_contents(lint_name, hint))?;
path.push("src");
fs::create_dir(&path)?;
write_file(path.join("main.rs"), get_test_file_contents(lint_name))?;
write_file(path.join("main.rs"), get_test_file_contents(lint_name, msrv))?;
Ok(())
}
@ -106,13 +112,25 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> {
let test_dir = lint.project_root.join(&relative_test_dir);
fs::create_dir(&test_dir)?;
create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?;
create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")?;
create_project_layout(
lint.name,
&test_dir,
"fail",
"Content that triggers the lint goes here",
msrv,
)?;
create_project_layout(
lint.name,
&test_dir,
"pass",
"This file should not trigger the lint",
false,
)?;
println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`");
} else {
let test_path = format!("tests/ui/{}.rs", lint.name);
let test_contents = get_test_file_contents(lint.name);
let test_contents = get_test_file_contents(lint.name, msrv);
write_file(lint.project_root.join(&test_path), test_contents)?;
println!("Generated test file: `{test_path}`");
@ -194,8 +212,8 @@ pub(crate) fn get_stabilization_version() -> String {
parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`")
}
fn get_test_file_contents(lint_name: &str) -> String {
formatdoc!(
fn get_test_file_contents(lint_name: &str, msrv: bool) -> String {
let mut test = formatdoc!(
r#"
#![warn(clippy::{lint_name})]
@ -203,7 +221,29 @@ fn get_test_file_contents(lint_name: &str) -> String {
// test code goes here
}}
"#
)
);
if msrv {
let _ = writedoc!(
test,
r#"
// TODO: set xx to the version one below the MSRV used by the lint, and yy to
// the version used by the lint
#[clippy::msrv = "1.xx"]
fn msrv_1_xx() {{
// a simple example that would trigger the lint if the MSRV were met
}}
#[clippy::msrv = "1.yy"]
fn msrv_1_yy() {{
// the same example as above
}}
"#
);
}
test
}
fn get_manifest_contents(lint_name: &str, hint: &str) -> String {
@ -258,7 +298,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
)
});
let _: fmt::Result = write!(result, "{}", get_lint_declaration(&name_upper, category));
let _: fmt::Result = writeln!(result, "{}", get_lint_declaration(&name_upper, category));
result.push_str(&if enable_msrv {
formatdoc!(
@ -281,7 +321,6 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
}}
// TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed.
// TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`.
// TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs`
"#
)

View File

@ -1,6 +1,6 @@
[package]
name = "clippy_lints"
version = "0.1.74"
version = "0.1.75"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
@ -28,6 +28,9 @@ semver = "1.0"
rustc-semver = "1.1"
url = "2.2"
[dev-dependencies]
walkdir = "2.3"
[features]
deny-warnings = ["clippy_utils/deny-warnings"]
# build clippy with internal lints enabled, off by default

View File

@ -8,6 +8,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of the `#[allow]` attribute and suggests replacing it with
/// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
///
@ -19,7 +20,6 @@ declare_clippy_lint! {
/// (`#![allow]`) are usually used to enable or disable lints on a global scale.
///
/// ### Why is this bad?
///
/// `#[expect]` attributes suppress the lint emission, but emit a warning, if
/// the expectation is unfulfilled. This can be useful to be notified when the
/// lint is no longer triggered.

View File

@ -229,6 +229,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO,
crate::items_after_test_module::ITEMS_AFTER_TEST_MODULE_INFO,
crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO,
crate::iter_without_into_iter::INTO_ITER_WITHOUT_ITER_INFO,
crate::iter_without_into_iter::ITER_WITHOUT_INTO_ITER_INFO,
crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO,
crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO,
crate::large_futures::LARGE_FUTURES_INFO,
@ -280,6 +282,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::manual_clamp::MANUAL_CLAMP_INFO,
crate::manual_float_methods::MANUAL_IS_FINITE_INFO,
crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
crate::manual_hash_one::MANUAL_HASH_ONE_INFO,
crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,

View File

@ -1,7 +1,7 @@
//! lint on C-like enums that are `repr(isize/usize)` and have values that
//! don't fit into an `i32`
use clippy_utils::consts::{miri_to_const, Constant};
use clippy_utils::consts::{mir_to_const, Constant};
use clippy_utils::diagnostics::span_lint;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
.const_eval_poly(def_id.to_def_id())
.ok()
.map(|val| rustc_middle::mir::Const::from_value(val, ty));
if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx, c)) {
if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx, c)) {
if let ty::Adt(adt, _) = ty.kind() {
if adt.is_enum() {
ty = adt.repr().discr_type().to_ty(cx.tcx);

View File

@ -27,7 +27,7 @@ declare_clippy_lint! {
///
/// impl std::error::Error for Error { ... }
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub ERROR_IMPL_ERROR,
restriction,
"exported types named `Error` that implement `Error`"

View File

@ -28,7 +28,7 @@ declare_clippy_lint! {
/// // ...
/// }
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub FOUR_FORWARD_SLASHES,
suspicious,
"comments with 4 forward slashes (`////`) likely intended to be doc comments (`///`)"

View File

@ -37,6 +37,10 @@ declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]);
impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns {
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) {
if pat.span.from_expansion() {
return;
}
match cx.tcx.hir().get_parent(pat.hir_id) {
Node::Param(param) if matches!(cx.tcx.hir().get_parent(param.hir_id), Node::Item(_)) => {
// Ignore function parameters

View File

@ -6,9 +6,8 @@ use rustc_hir as hir;
use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor};
use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{Ty, TypeckResults};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
@ -162,7 +161,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
vis.visit_ty(ty);
for target in &vis.found {
if in_external_macro(cx.sess(), generics.span) {
if generics.span.from_expansion() {
continue;
}
let generics_suggestion_span = generics.span.substitute_dummy({

View File

@ -1,10 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::{is_from_proc_macro, is_in_cfg_test};
use rustc_hir::{HirId, ItemId, ItemKind, Mod};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_from_proc_macro};
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_hir::{HirId, Item, ItemKind, Mod};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Span};
use rustc_span::hygiene::AstPass;
use rustc_span::{sym, ExpnKind};
declare_clippy_lint! {
/// ### What it does
@ -41,46 +43,72 @@ declare_clippy_lint! {
declare_lint_pass!(ItemsAfterTestModule => [ITEMS_AFTER_TEST_MODULE]);
fn cfg_test_module<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool {
if let ItemKind::Mod(test_mod) = item.kind
&& item.span.hi() == test_mod.spans.inner_span.hi()
&& is_cfg_test(cx.tcx, item.hir_id())
&& !item.span.from_expansion()
&& !is_from_proc_macro(cx, item)
{
true
} else {
false
}
}
impl LateLintPass<'_> for ItemsAfterTestModule {
fn check_mod(&mut self, cx: &LateContext<'_>, _: &Mod<'_>, _: HirId) {
let mut was_test_mod_visited = false;
let mut test_mod_span: Option<Span> = None;
fn check_mod(&mut self, cx: &LateContext<'_>, module: &Mod<'_>, _: HirId) {
let mut items = module.item_ids.iter().map(|&id| cx.tcx.hir().item(id));
let hir = cx.tcx.hir();
let items = hir.items().collect::<Vec<ItemId>>();
let Some((mod_pos, test_mod)) = items.by_ref().enumerate().find(|(_, item)| cfg_test_module(cx, item)) else {
return;
};
for (i, itid) in items.iter().enumerate() {
let item = hir.item(*itid);
let after: Vec<_> = items
.filter(|item| {
// Ignore the generated test main function
!(item.ident.name == sym::main
&& item.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::TestHarness))
})
.collect();
if_chain! {
if was_test_mod_visited;
if i == (items.len() - 3 /* Weird magic number (HIR-translation behaviour) */);
if cx.sess().source_map().lookup_char_pos(item.span.lo()).file.name_hash
== cx.sess().source_map().lookup_char_pos(test_mod_span.unwrap().lo()).file.name_hash; // Will never fail
if !matches!(item.kind, ItemKind::Mod(_));
if !is_in_cfg_test(cx.tcx, itid.hir_id()); // The item isn't in the testing module itself
if !in_external_macro(cx.sess(), item.span);
if !is_from_proc_macro(cx, item);
if let Some(last) = after.last()
&& after.iter().all(|&item| {
!matches!(item.kind, ItemKind::Mod(_))
&& !item.span.from_expansion()
&& !is_from_proc_macro(cx, item)
})
&& !fulfill_or_allowed(cx, ITEMS_AFTER_TEST_MODULE, after.iter().map(|item| item.hir_id()))
{
let def_spans: Vec<_> = std::iter::once(test_mod.owner_id)
.chain(after.iter().map(|item| item.owner_id))
.map(|id| cx.tcx.def_span(id))
.collect();
then {
span_lint_and_help(cx, ITEMS_AFTER_TEST_MODULE, test_mod_span.unwrap().with_hi(item.span.hi()), "items were found after the testing module", None, "move the items to before the testing module was defined");
}};
if let ItemKind::Mod(module) = item.kind && item.span.hi() == module.spans.inner_span.hi() {
// Check that it works the same way, the only I way I've found for #10713
for attr in cx.tcx.get_attrs(item.owner_id.to_def_id(), sym::cfg) {
if_chain! {
if attr.has_name(sym::cfg);
if let Some(mitems) = attr.meta_item_list();
if let [mitem] = &*mitems;
if mitem.has_name(sym::test);
then {
was_test_mod_visited = true;
test_mod_span = Some(item.span);
}
span_lint_hir_and_then(
cx,
ITEMS_AFTER_TEST_MODULE,
test_mod.hir_id(),
def_spans,
"items after a test module",
|diag| {
if let Some(prev) = mod_pos.checked_sub(1)
&& let prev = cx.tcx.hir().item(module.item_ids[prev])
&& let items_span = last.span.with_lo(test_mod.span.hi())
&& let Some(items) = snippet_opt(cx, items_span)
{
diag.multipart_suggestion_with_style(
"move the items to before the test module was defined",
vec![
(prev.span.shrink_to_hi(), items),
(items_span, String::new())
],
Applicability::MachineApplicable,
SuggestionStyle::HideCodeAlways,
);
}
}
}
},
);
}
}
}

View File

@ -0,0 +1,249 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::get_parent_as_impl;
use clippy_utils::source::snippet;
use clippy_utils::ty::{implements_trait, make_normalized_projection};
use rustc_ast::Mutability;
use rustc_errors::Applicability;
use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Symbol};
declare_clippy_lint! {
/// ### What it does
/// Looks for `iter` and `iter_mut` methods without an associated `IntoIterator for (&|&mut) Type` implementation.
///
/// ### Why is this bad?
/// It's not bad, but having them is idiomatic and allows the type to be used in for loops directly
/// (`for val in &iter {}`), without having to first call `iter()` or `iter_mut()`.
///
/// ### Example
/// ```rust
/// struct MySlice<'a>(&'a [u8]);
/// impl<'a> MySlice<'a> {
/// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
/// self.0.iter()
/// }
/// }
/// ```
/// Use instead:
/// ```rust
/// struct MySlice<'a>(&'a [u8]);
/// impl<'a> MySlice<'a> {
/// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
/// self.0.iter()
/// }
/// }
/// impl<'a> IntoIterator for &MySlice<'a> {
/// type Item = &'a u8;
/// type IntoIter = std::slice::Iter<'a, u8>;
/// fn into_iter(self) -> Self::IntoIter {
/// self.iter()
/// }
/// }
/// ```
#[clippy::version = "1.74.0"]
pub ITER_WITHOUT_INTO_ITER,
pedantic,
"implementing `iter(_mut)` without an associated `IntoIterator for (&|&mut) Type` impl"
}
declare_clippy_lint! {
/// ### What it does
/// This is the opposite of the `iter_without_into_iter` lint.
/// It looks for `IntoIterator for (&|&mut) Type` implementations without an inherent `iter` or `iter_mut` method.
///
/// ### Why is this bad?
/// It's not bad, but having them is idiomatic and allows the type to be used in iterator chains
/// by just calling `.iter()`, instead of the more awkward `<&Type>::into_iter` or `(&val).iter()` syntax
/// in case of ambiguity with another `Intoiterator` impl.
///
/// ### Example
/// ```rust
/// struct MySlice<'a>(&'a [u8]);
/// impl<'a> IntoIterator for &MySlice<'a> {
/// type Item = &'a u8;
/// type IntoIter = std::slice::Iter<'a, u8>;
/// fn into_iter(self) -> Self::IntoIter {
/// self.0.iter()
/// }
/// }
/// ```
/// Use instead:
/// ```rust
/// struct MySlice<'a>(&'a [u8]);
/// impl<'a> MySlice<'a> {
/// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
/// self.into_iter()
/// }
/// }
/// impl<'a> IntoIterator for &MySlice<'a> {
/// type Item = &'a u8;
/// type IntoIter = std::slice::Iter<'a, u8>;
/// fn into_iter(self) -> Self::IntoIter {
/// self.0.iter()
/// }
/// }
/// ```
#[clippy::version = "1.74.0"]
pub INTO_ITER_WITHOUT_ITER,
pedantic,
"implementing `IntoIterator for (&|&mut) Type` without an inherent `iter(_mut)` method"
}
declare_lint_pass!(IterWithoutIntoIter => [ITER_WITHOUT_INTO_ITER, INTO_ITER_WITHOUT_ITER]);
/// Checks if a given type is nameable in a trait (impl).
/// RPIT is stable, but impl Trait in traits is not (yet), so when we have
/// a function such as `fn iter(&self) -> impl IntoIterator`, we can't
/// suggest `type IntoIter = impl IntoIterator`.
fn is_nameable_in_impl_trait(ty: &rustc_hir::Ty<'_>) -> bool {
!matches!(ty.kind, TyKind::OpaqueDef(..))
}
fn type_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool {
if let Some(ty_did) = ty.ty_adt_def().map(ty::AdtDef::did) {
cx.tcx.inherent_impls(ty_did).iter().any(|&did| {
cx.tcx
.associated_items(did)
.filter_by_name_unhygienic(method_name)
.next()
.is_some_and(|item| item.kind == ty::AssocKind::Fn)
})
} else {
false
}
}
impl LateLintPass<'_> for IterWithoutIntoIter {
fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
if let ItemKind::Impl(imp) = item.kind
&& let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind
&& let Some(trait_ref) = imp.of_trait
&& trait_ref.trait_def_id().is_some_and(|did| cx.tcx.is_diagnostic_item(sym::IntoIterator, did))
&& let &ty::Ref(_, ty, mtbl) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind()
&& let expected_method_name = match mtbl {
Mutability::Mut => sym::iter_mut,
Mutability::Not => sym::iter,
}
&& !type_has_inherent_method(cx, ty, expected_method_name)
&& let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
if item.ident.name == sym!(IntoIter) {
Some(cx.tcx.hir().impl_item(item.id).expect_type().span)
} else {
None
}
})
{
span_lint_and_then(
cx,
INTO_ITER_WITHOUT_ITER,
item.span,
&format!("`IntoIterator` implemented for a reference type without an `{expected_method_name}` method"),
|diag| {
// The suggestion forwards to the `IntoIterator` impl and uses a form of UFCS
// to avoid name ambiguities, as there might be an inherent into_iter method
// that we don't want to call.
let sugg = format!(
"
impl {self_ty_without_ref} {{
fn {expected_method_name}({ref_self}self) -> {iter_ty} {{
<{ref_self}Self as IntoIterator>::into_iter(self)
}}
}}
",
self_ty_without_ref = snippet(cx, self_ty_without_ref.ty.span, ".."),
ref_self = mtbl.ref_prefix_str(),
iter_ty = snippet(cx, iter_assoc_span, ".."),
);
diag.span_suggestion_verbose(
item.span.shrink_to_lo(),
format!("consider implementing `{expected_method_name}`"),
sugg,
// Just like iter_without_into_iter, this suggestion is on a best effort basis
// and requires potentially adding lifetimes or moving them around.
Applicability::Unspecified
);
}
);
}
}
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) {
let item_did = item.owner_id.to_def_id();
let (borrow_prefix, expected_implicit_self) = match item.ident.name {
sym::iter => ("&", ImplicitSelfKind::ImmRef),
sym::iter_mut => ("&mut ", ImplicitSelfKind::MutRef),
_ => return,
};
if let ImplItemKind::Fn(sig, _) = item.kind
&& let FnRetTy::Return(ret) = sig.decl.output
&& is_nameable_in_impl_trait(ret)
&& cx.tcx.generics_of(item_did).params.is_empty()
&& sig.decl.implicit_self == expected_implicit_self
&& sig.decl.inputs.len() == 1
&& let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id())
&& imp.of_trait.is_none()
&& let sig = cx.tcx.liberate_late_bound_regions(
item_did,
cx.tcx.fn_sig(item_did).instantiate_identity()
)
&& let ref_ty = sig.inputs()[0]
&& let Some(into_iter_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
&& let Some(iterator_did) = cx.tcx.get_diagnostic_item(sym::Iterator)
&& let ret_ty = sig.output()
// Order is important here, we need to check that the `fn iter` return type actually implements `IntoIterator`
// *before* normalizing `<_ as IntoIterator>::Item` (otherwise make_normalized_projection ICEs)
&& implements_trait(cx, ret_ty, iterator_did, &[])
&& let Some(iter_ty) = make_normalized_projection(
cx.tcx,
cx.param_env,
iterator_did,
sym!(Item),
[ret_ty],
)
// Only lint if the `IntoIterator` impl doesn't actually exist
&& !implements_trait(cx, ref_ty, into_iter_did, &[])
{
let self_ty_snippet = format!("{borrow_prefix}{}", snippet(cx, imp.self_ty.span, ".."));
span_lint_and_then(
cx,
ITER_WITHOUT_INTO_ITER,
item.span,
&format!("`{}` method without an `IntoIterator` impl for `{self_ty_snippet}`", item.ident),
|diag| {
// Get the lower span of the `impl` block, and insert the suggestion right before it:
// impl X {
// ^ fn iter(&self) -> impl IntoIterator { ... }
// }
let span_behind_impl = cx.tcx
.def_span(cx.tcx.hir().parent_id(item.hir_id()).owner.def_id)
.shrink_to_lo();
let sugg = format!(
"
impl IntoIterator for {self_ty_snippet} {{
type IntoIter = {ret_ty};
type Iter = {iter_ty};
fn into_iter() -> Self::IntoIter {{
self.iter()
}}
}}
"
);
diag.span_suggestion_verbose(
span_behind_impl,
format!("consider implementing `IntoIterator` for `{self_ty_snippet}`"),
sugg,
// Suggestion is on a best effort basis, might need some adjustments by the user
// such as adding some lifetimes in the associated types, or importing types.
Applicability::Unspecified,
);
});
}
}
}

View File

@ -169,6 +169,7 @@ mod invalid_upcast_comparisons;
mod items_after_statements;
mod items_after_test_module;
mod iter_not_returning_iterator;
mod iter_without_into_iter;
mod large_const_arrays;
mod large_enum_variant;
mod large_futures;
@ -190,6 +191,7 @@ mod manual_async_fn;
mod manual_bits;
mod manual_clamp;
mod manual_float_methods;
mod manual_hash_one;
mod manual_is_ascii_check;
mod manual_let_else;
mod manual_main_separator_str;
@ -1119,6 +1121,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
msrv(),
))
});
store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(msrv())));
store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View File

@ -26,7 +26,7 @@ declare_clippy_lint! {
/// # let x = 1.0f32;
/// if x.is_infinite() {}
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub MANUAL_IS_INFINITE,
style,
"use dedicated method to check if a float is infinite"
@ -51,7 +51,7 @@ declare_clippy_lint! {
/// if x.is_finite() {}
/// if x.is_finite() {}
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub MANUAL_IS_FINITE,
style,
"use dedicated method to check if a float is finite"

View File

@ -0,0 +1,133 @@
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
use clippy_utils::visitors::{is_local_used, local_used_once};
use clippy_utils::{is_trait_method, path_to_local_id};
use rustc_errors::Applicability;
use rustc_hir::{BindingAnnotation, ExprKind, Local, Node, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
/// Checks for cases where [`BuildHasher::hash_one`] can be used.
///
/// [`BuildHasher::hash_one`]: https://doc.rust-lang.org/std/hash/trait.BuildHasher.html#method.hash_one
///
/// ### Why is this bad?
/// It is more concise to use the `hash_one` method.
///
/// ### Example
/// ```rust
/// use std::hash::{BuildHasher, Hash, Hasher};
/// use std::collections::hash_map::RandomState;
///
/// let s = RandomState::new();
/// let value = vec![1, 2, 3];
///
/// let mut hasher = s.build_hasher();
/// value.hash(&mut hasher);
/// let hash = hasher.finish();
/// ```
/// Use instead:
/// ```rust
/// use std::hash::BuildHasher;
/// use std::collections::hash_map::RandomState;
///
/// let s = RandomState::new();
/// let value = vec![1, 2, 3];
///
/// let hash = s.hash_one(&value);
/// ```
#[clippy::version = "1.74.0"]
pub MANUAL_HASH_ONE,
complexity,
"manual implementations of `BuildHasher::hash_one`"
}
pub struct ManualHashOne {
msrv: Msrv,
}
impl ManualHashOne {
#[must_use]
pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl_lint_pass!(ManualHashOne => [MANUAL_HASH_ONE]);
impl LateLintPass<'_> for ManualHashOne {
fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
// `let mut hasher = seg.build_hasher();`
if let PatKind::Binding(BindingAnnotation::MUT, hasher, _, None) = local.pat.kind
&& let Some(init) = local.init
&& !init.span.from_expansion()
&& let ExprKind::MethodCall(seg, build_hasher, [], _) = init.kind
&& seg.ident.name == sym!(build_hasher)
&& let Node::Stmt(local_stmt) = cx.tcx.hir().get_parent(local.hir_id)
&& let Node::Block(block) = cx.tcx.hir().get_parent(local_stmt.hir_id)
&& let mut stmts = block.stmts.iter()
.skip_while(|stmt| stmt.hir_id != local_stmt.hir_id)
.skip(1)
.filter(|&stmt| is_local_used(cx, stmt, hasher))
// `hashed_value.hash(&mut hasher);`
&& let Some(hash_stmt) = stmts.next()
&& let StmtKind::Semi(hash_expr) = hash_stmt.kind
&& !hash_expr.span.from_expansion()
&& let ExprKind::MethodCall(seg, hashed_value, [ref_to_hasher], _) = hash_expr.kind
&& seg.ident.name == sym::hash
&& is_trait_method(cx, hash_expr, sym::Hash)
&& path_to_local_id(ref_to_hasher.peel_borrows(), hasher)
&& let maybe_finish_stmt = stmts.next()
// There should be no more statements referencing `hasher`
&& stmts.next().is_none()
// `hasher.finish()`, may be anywhere in a statement or the trailing expr of the block
&& let Some(path_expr) = local_used_once(cx, (maybe_finish_stmt, block.expr), hasher)
&& let Node::Expr(finish_expr) = cx.tcx.hir().get_parent(path_expr.hir_id)
&& !finish_expr.span.from_expansion()
&& let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind
&& seg.ident.name == sym!(finish)
&& self.msrv.meets(msrvs::BUILD_HASHER_HASH_ONE)
{
span_lint_hir_and_then(
cx,
MANUAL_HASH_ONE,
finish_expr.hir_id,
finish_expr.span,
"manual implementation of `BuildHasher::hash_one`",
|diag| {
if let Some(build_hasher) = snippet_opt(cx, build_hasher.span)
&& let Some(hashed_value) = snippet_opt(cx, hashed_value.span)
{
diag.multipart_suggestion(
"try",
vec![
(local_stmt.span, String::new()),
(hash_stmt.span, String::new()),
(
finish_expr.span,
// `needless_borrows_for_generic_args` will take care of
// removing the `&` when it isn't needed
format!("{build_hasher}.hash_one(&{hashed_value})")
)
],
Applicability::MachineApplicable,
);
}
},
);
}
}
extract_msrv_attr!(LateContext);
}

View File

@ -136,9 +136,9 @@ fn emit_manual_let_else(
// for this to be machine applicable.
let mut app = Applicability::HasPlaceholders;
let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app);
let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app);
let (sn_else, else_is_mac_call) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app);
let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) {
let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) && !else_is_mac_call {
sn_else.into_owned()
} else {
format!("{{ {sn_else} }}")

View File

@ -3,6 +3,7 @@ use clippy_utils::is_doc_hidden;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
use rustc_ast::ast::{self, VisibilityKind};
use rustc_ast::attr;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
@ -158,7 +159,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
let mut iter = def.variants.iter().filter_map(|v| {
(matches!(v.data, hir::VariantData::Unit(_, _))
&& v.ident.as_str().starts_with('_')
&& is_doc_hidden(cx.tcx.hir().attrs(v.hir_id)))
&& is_doc_hidden(cx.tcx.hir().attrs(v.hir_id))
&& !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive))
.then_some((v.def_id, v.span))
});
if let Some((id, span)) = iter.next()
@ -198,16 +200,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
enum_span,
"this seems like a manual implementation of the non-exhaustive pattern",
|diag| {
if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive()
&& let header_span = cx.sess().source_map().span_until_char(enum_span, '{')
&& let Some(snippet) = snippet_opt(cx, header_span)
{
diag.span_suggestion(
header_span,
"add the attribute",
format!("#[non_exhaustive] {snippet}"),
Applicability::Unspecified,
);
let header_span = cx.sess().source_map().span_until_char(enum_span, '{');
if let Some(snippet) = snippet_opt(cx, header_span) {
diag.span_suggestion(
header_span,
"add the attribute",
format!("#[non_exhaustive] {snippet}"),
Applicability::Unspecified,
);
}
diag.span_help(variant_span, "remove this variant");
},

View File

@ -961,7 +961,7 @@ declare_clippy_lint! {
/// _ => todo!(),
/// }
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub REDUNDANT_GUARDS,
complexity,
"checks for unnecessary guards in match expressions"

View File

@ -1,4 +1,4 @@
use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt};
use clippy_utils::consts::{constant, constant_full_int, mir_to_const, FullInt};
use clippy_utils::diagnostics::span_lint_and_note;
use core::cmp::Ordering;
use rustc_hir::{Arm, Expr, PatKind, RangeEnd};
@ -37,14 +37,14 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
Some(lhs) => constant(cx, cx.typeck_results(), lhs)?,
None => {
let min_val_const = ty.numeric_min_val(cx.tcx)?;
miri_to_const(cx, mir::Const::from_ty_const(min_val_const, cx.tcx))?
mir_to_const(cx, mir::Const::from_ty_const(min_val_const, cx.tcx))?
},
};
let rhs_const = match rhs {
Some(rhs) => constant(cx, cx.typeck_results(), rhs)?,
None => {
let max_val_const = ty.numeric_max_val(cx.tcx)?;
miri_to_const(cx, mir::Const::from_ty_const(max_val_const, cx.tcx))?
mir_to_const(cx, mir::Const::from_ty_const(max_val_const, cx.tcx))?
},
};
let lhs_val = lhs_const.int_value(cx, ty)?;

View File

@ -2970,7 +2970,7 @@ declare_clippy_lint! {
/// assert_eq!((*any_box).type_id(), TypeId::of::<i32>());
/// // ^ dereference first, to call `type_id` on `dyn Any`
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub TYPE_ID_ON_BOX,
suspicious,
"calling `.type_id()` on `Box<dyn Any>`"
@ -3368,6 +3368,7 @@ declare_clippy_lint! {
}
declare_clippy_lint! {
/// ### What it does
/// Looks for calls to [`Stdin::read_line`] to read a line from the standard input
/// into a string, then later attempting to parse this string into a type without first trimming it, which will
/// always fail because the string has a trailing newline in it.
@ -3390,7 +3391,7 @@ declare_clippy_lint! {
/// // ^^^^^^^^^^^ remove the trailing newline
/// assert_eq!(num, 42);
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub READ_LINE_WITHOUT_TRIM,
correctness,
"calling `Stdin::read_line`, then trying to parse it without first trimming"
@ -3418,7 +3419,7 @@ declare_clippy_lint! {
/// # let c = 'c';
/// matches!(c, '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub STRING_LIT_CHARS_ANY,
restriction,
"checks for `<string_lit>.chars().any(|i| i == c)`"
@ -3453,7 +3454,7 @@ declare_clippy_lint! {
/// })
/// }
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub FORMAT_COLLECT,
perf,
"`format!`ing every element in a collection, then collecting the strings into a new `String`"
@ -3474,7 +3475,7 @@ declare_clippy_lint! {
/// let y = v.iter().collect::<Vec<_>>();
/// assert_eq!(x, y);
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub ITER_SKIP_ZERO,
correctness,
"disallows `.skip(0)`"
@ -3505,7 +3506,7 @@ declare_clippy_lint! {
/// # let v = vec![];
/// _ = v.into_iter().filter(|i| i % 2 == 0).map(|i| really_expensive_fn(i));
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub FILTER_MAP_BOOL_THEN,
style,
"checks for usage of `bool::then` in `Iterator::filter_map`"

View File

@ -24,7 +24,6 @@ fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) -
}
}
#[expect(clippy::too_many_lines)]
pub(super) fn check(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
@ -101,11 +100,11 @@ pub(super) fn check(
(expr.span.with_lo(args[0].span.hi()), String::new()),
]),
("None", "unwrap_or_else", _) => match args[0].kind {
hir::ExprKind::Closure(hir::Closure {
body,
..
}) => Some(vec![
(expr.span.with_hi(cx.tcx.hir().body(*body).value.span.lo()), String::new()),
hir::ExprKind::Closure(hir::Closure { body, .. }) => Some(vec![
(
expr.span.with_hi(cx.tcx.hir().body(*body).value.span.lo()),
String::new(),
),
(expr.span.with_lo(args[0].span.hi()), String::new()),
]),
_ => None,

View File

@ -15,6 +15,10 @@ declare_clippy_lint! {
/// A good custom message should be more about why the failure of the assertion is problematic
/// and not what is failed because the assertion already conveys that.
///
/// Although the same reasoning applies to testing functions, this lint ignores them as they would be too noisy.
/// Also, in most cases understanding the test failure would be easier
/// compared to understanding a complex invariant distributed around the codebase.
///
/// ### Known problems
/// This lint cannot check the quality of the custom panic messages.
/// Hence, you can suppress this lint simply by adding placeholder messages

View File

@ -135,10 +135,7 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
}
fn stmt_might_diverge(stmt: &Stmt<'_>) -> bool {
match stmt.kind {
StmtKind::Item(..) => false,
_ => true,
}
!matches!(stmt.kind, StmtKind::Item(..))
}
impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
@ -148,7 +145,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
ExprKind::Block(block, ..) => match (block.stmts, block.expr) {
(stmts, Some(e)) => {
if stmts.iter().all(|stmt| !stmt_might_diverge(stmt)) {
self.visit_expr(e)
self.visit_expr(e);
}
},
([first @ .., stmt], None) => {

View File

@ -47,9 +47,9 @@ declare_clippy_lint! {
/// 12 + *y
/// }
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub NEEDLESS_PASS_BY_REF_MUT,
suspicious,
nursery,
"using a `&mut` argument when it's not mutated"
}

View File

@ -102,7 +102,7 @@ declare_clippy_lint! {
/// }
/// }
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub NON_CANONICAL_PARTIAL_ORD_IMPL,
suspicious,
"non-canonical implementation of `PartialOrd` on an `Ord` type"

View File

@ -312,7 +312,7 @@ declare_clippy_lint! {
/// # let status_code = 200;
/// if status_code <= 400 && status_code > 500 {}
/// ```
#[clippy::version = "1.71.0"]
#[clippy::version = "1.73.0"]
pub IMPOSSIBLE_COMPARISONS,
correctness,
"double comparisons that will never evaluate to `true`"
@ -332,7 +332,7 @@ declare_clippy_lint! {
/// # let status_code = 200;
/// if status_code <= 400 && status_code < 500 {}
/// ```
#[clippy::version = "1.71.0"]
#[clippy::version = "1.73.0"]
pub REDUNDANT_COMPARISONS,
correctness,
"double comparisons where one of them can be removed"

View File

@ -75,6 +75,7 @@ impl EarlyLintPass for RawStrings {
if !snippet(cx, expr.span, prefix).trim().starts_with(prefix) {
return;
}
let descr = lit.kind.descr();
if !str.contains(['\\', '"']) {
span_lint_and_then(
@ -89,20 +90,17 @@ impl EarlyLintPass for RawStrings {
let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1);
let start = start.with_lo(r_pos);
if end.is_empty() {
diag.span_suggestion(
start,
"use a string literal instead",
format!("\"{str}\""),
Applicability::MachineApplicable,
);
} else {
diag.multipart_suggestion(
"try",
vec![(start, String::new()), (end, String::new())],
Applicability::MachineApplicable,
);
let mut remove = vec![(start, String::new())];
// avoid debug ICE from empty suggestions
if !end.is_empty() {
remove.push((end, String::new()));
}
diag.multipart_suggestion_verbose(
format!("use a plain {descr} literal instead"),
remove,
Applicability::MachineApplicable,
);
},
);
if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) {
@ -149,9 +147,9 @@ impl EarlyLintPass for RawStrings {
let (start, end) = hash_spans(expr.span, prefix, req, max);
let message = match max - req {
_ if req == 0 => "remove all the hashes around the literal".to_string(),
1 => "remove one hash from both sides of the literal".to_string(),
n => format!("remove {n} hashes from both sides of the literal"),
_ if req == 0 => format!("remove all the hashes around the {descr} literal"),
1 => format!("remove one hash from both sides of the {descr} literal"),
n => format!("remove {n} hashes from both sides of the {descr} literal"),
};
diag.multipart_suggestion(

View File

@ -3,11 +3,9 @@ use clippy_utils::is_from_proc_macro;
use clippy_utils::ty::needs_ordered_drop;
use rustc_ast::Mutability;
use rustc_hir::def::Res;
use rustc_hir::{
BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath,
};
use rustc_hir::{BindingAnnotation, ByRef, ExprKind, HirId, Local, Node, Pat, PatKind, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::{in_external_macro, is_from_async_await};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Ident;
use rustc_span::DesugaringKind;
@ -39,7 +37,7 @@ declare_clippy_lint! {
/// // no redefinition with the same name
/// }
/// ```
#[clippy::version = "1.72.0"]
#[clippy::version = "1.73.0"]
pub REDUNDANT_LOCALS,
correctness,
"redundant redefinition of a local binding"
@ -68,21 +66,18 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals {
// the local does not change the effect of assignments to the binding. see #11290
if !affects_assignments(cx, mutability, binding_id, local.hir_id);
// the local does not affect the code's drop behavior
if !affects_drop_behavior(cx, binding_id, local.hir_id, expr);
if !needs_ordered_drop(cx, cx.typeck_results().expr_ty(expr));
// the local is user-controlled
if !in_external_macro(cx.sess(), local.span);
if !is_from_proc_macro(cx, expr);
// Async function parameters are lowered into the closure body, so we can't lint them.
// see `lower_maybe_async_body` in `rust_ast_lowering`
if !is_from_async_await(local.span);
then {
span_lint_and_help(
cx,
REDUNDANT_LOCALS,
vec![binding_pat.span, local.span],
"redundant redefinition of a binding",
None,
&format!("remove the redefinition of `{ident}`"),
local.span,
&format!("redundant redefinition of a binding `{ident}`"),
Some(binding_pat.span),
&format!("`{ident}` is initially defined here"),
);
}
}
@ -109,18 +104,3 @@ fn affects_assignments(cx: &LateContext<'_>, mutability: Mutability, bind: HirId
// the binding is mutable and the rebinding is in a different scope than the original binding
mutability == Mutability::Mut && hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind)
}
/// Check if a rebinding of a local affects the code's drop behavior.
fn affects_drop_behavior<'tcx>(
cx: &LateContext<'tcx>,
bind: HirId,
rebind: HirId,
rebind_expr: &Expr<'tcx>,
) -> bool {
let hir = cx.tcx.hir();
// the rebinding is in a different scope than the original binding
// and the type of the binding cares about drop order
hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind)
&& needs_ordered_drop(cx, cx.typeck_results().expr_ty(rebind_expr))
}

View File

@ -1,9 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_from_proc_macro;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::def_id::DefId;
use rustc_hir::{HirId, Path, PathSegment};
use rustc_lint::{LateContext, LateLintPass};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::kw;
use rustc_span::{sym, Span};
@ -99,6 +101,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
if let Res::Def(_, def_id) = path.res
&& let Some(first_segment) = get_first_segment(path)
&& is_stable(cx, def_id)
&& !in_external_macro(cx.sess(), path.span)
&& !is_from_proc_macro(cx, &first_segment.ident)
{
let (lint, used_mod, replace_with) = match first_segment.ident.name {
sym::std => match cx.tcx.crate_name(def_id.krate) {

View File

@ -294,7 +294,7 @@ define_Conf! {
///
/// Suppress lints whenever the suggested change would cause breakage for other crates.
(avoid_breaking_exported_api: bool = true),
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD.
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE.
///
/// The minimum rust version that the project supports
(msrv: Option<String> = None),
@ -744,3 +744,44 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec<usize>) {
(rows, column_widths)
}
#[cfg(test)]
mod tests {
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use serde::de::IgnoredAny;
use std::fs;
use walkdir::WalkDir;
#[test]
fn configs_are_tested() {
let mut names: FxHashSet<String> = super::metadata::get_configuration_metadata()
.into_iter()
.map(|meta| meta.name.replace('_', "-"))
.collect();
let toml_files = WalkDir::new("../tests")
.into_iter()
.map(Result::unwrap)
.filter(|entry| entry.file_name() == "clippy.toml");
for entry in toml_files {
let file = fs::read_to_string(entry.path()).unwrap();
#[allow(clippy::zero_sized_map_values)]
if let Ok(map) = toml::from_str::<FxHashMap<String, IgnoredAny>>(&file) {
for name in map.keys() {
names.remove(name.as_str());
}
}
}
assert!(
names.remove("allow-one-hash-in-raw-strings"),
"remove this when #11481 is fixed"
);
assert!(
names.is_empty(),
"Configuration variable lacks test: {names:?}\nAdd a test to `tests/ui-toml`"
);
}
}

View File

@ -132,6 +132,7 @@ impl LateLintPass<'_> for WildcardImports {
if self.warn_on_all || !self.check_exceptions(item, use_path.segments);
let used_imports = cx.tcx.names_imported_by_glob_use(item.owner_id.def_id);
if !used_imports.is_empty(); // Already handled by `unused_imports`
if !used_imports.contains(&kw::Underscore);
then {
let mut applicability = Applicability::MachineApplicable;
let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability);

View File

@ -3,12 +3,15 @@ use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro
use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
use clippy_utils::{is_in_cfg_test, is_in_test_function};
use rustc_ast::token::LitKind;
use rustc_ast::{FormatArgPosition, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatTrait};
use rustc_ast::{
FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder,
FormatTrait,
};
use rustc_errors::Applicability;
use rustc_hir::{Expr, Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, BytePos};
use rustc_span::{sym, BytePos, Span};
declare_clippy_lint! {
/// ### What it does
@ -450,6 +453,12 @@ fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call
fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos);
let lint_name = if name.starts_with("write") {
WRITE_LITERAL
} else {
PRINT_LITERAL
};
let mut counts = vec![0u32; format_args.arguments.all_args().len()];
for piece in &format_args.template {
if let FormatArgsPiece::Placeholder(placeholder) = piece {
@ -457,6 +466,12 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
}
}
let mut suggestion: Vec<(Span, String)> = vec![];
// holds index of replaced positional arguments; used to decrement the index of the remaining
// positional arguments.
let mut replaced_position: Vec<usize> = vec![];
let mut sug_span: Option<Span> = None;
for piece in &format_args.template {
if let FormatArgsPiece::Placeholder(FormatPlaceholder {
argument,
@ -471,9 +486,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
&& let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
&& !arg.expr.span.from_expansion()
&& let Some(value_string) = snippet_opt(cx, arg.expr.span)
{
{
let (replacement, replace_raw) = match lit.kind {
LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
Some(extracted) => extracted,
None => return,
},
@ -493,12 +508,6 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
_ => continue,
};
let lint = if name.starts_with("write") {
WRITE_LITERAL
} else {
PRINT_LITERAL
};
let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { continue };
let format_string_is_raw = format_string_snippet.starts_with('r');
@ -519,29 +528,58 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
},
};
span_lint_and_then(
cx,
lint,
arg.expr.span,
"literal with an empty format string",
|diag| {
if let Some(replacement) = replacement
// `format!("{}", "a")`, `format!("{named}", named = "b")
// ~~~~~ ~~~~~~~~~~~~~
&& let Some(removal_span) = format_arg_removal_span(format_args, index)
{
let replacement = replacement.replace('{', "{{").replace('}', "}}");
diag.multipart_suggestion(
"try",
vec![(*placeholder_span, replacement), (removal_span, String::new())],
Applicability::MachineApplicable,
);
}
},
);
sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span));
if let Some((_, index)) = positional_arg_piece_span(piece) {
replaced_position.push(index);
}
if let Some(replacement) = replacement
// `format!("{}", "a")`, `format!("{named}", named = "b")
// ~~~~~ ~~~~~~~~~~~~~
&& let Some(removal_span) = format_arg_removal_span(format_args, index) {
let replacement = escape_braces(&replacement, !format_string_is_raw && !replace_raw);
suggestion.push((*placeholder_span, replacement));
suggestion.push((removal_span, String::new()));
}
}
}
// Decrement the index of the remaining by the number of replaced positional arguments
if !suggestion.is_empty() {
for piece in &format_args.template {
if let Some((span, index)) = positional_arg_piece_span(piece)
&& suggestion.iter().all(|(s, _)| *s != span) {
let decrement = replaced_position.iter().filter(|i| **i < index).count();
suggestion.push((span, format!("{{{}}}", index.saturating_sub(decrement))));
}
}
}
if let Some(span) = sug_span {
span_lint_and_then(cx, lint_name, span, "literal with an empty format string", |diag| {
if !suggestion.is_empty() {
diag.multipart_suggestion("try", suggestion, Applicability::MachineApplicable);
}
});
}
}
/// Extract Span and its index from the given `piece`, iff it's positional argument.
fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
match piece {
FormatArgsPiece::Placeholder(FormatPlaceholder {
argument:
FormatArgPosition {
index: Ok(index),
kind: FormatArgPositionKind::Number,
..
},
span: Some(span),
..
}) => Some((*span, *index)),
_ => None,
}
}
/// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw
@ -593,3 +631,47 @@ fn conservative_unescape(literal: &str) -> Result<String, UnescapeErr> {
if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) }
}
/// Replaces `{` with `{{` and `}` with `}}`. If `preserve_unicode_escapes` is `true` the braces in
/// `\u{xxxx}` are left unmodified
#[expect(clippy::match_same_arms)]
fn escape_braces(literal: &str, preserve_unicode_escapes: bool) -> String {
#[derive(Clone, Copy)]
enum State {
Normal,
Backslash,
UnicodeEscape,
}
let mut escaped = String::with_capacity(literal.len());
let mut state = State::Normal;
for ch in literal.chars() {
state = match (ch, state) {
// Escape braces outside of unicode escapes by doubling them up
('{' | '}', State::Normal) => {
escaped.push(ch);
State::Normal
},
// If `preserve_unicode_escapes` isn't enabled stay in `State::Normal`, otherwise:
//
// \u{aaaa} \\ \x01
// ^ ^ ^
('\\', State::Normal) if preserve_unicode_escapes => State::Backslash,
// \u{aaaa}
// ^
('u', State::Backslash) => State::UnicodeEscape,
// \xAA \\
// ^ ^
(_, State::Backslash) => State::Normal,
// \u{aaaa}
// ^
('}', State::UnicodeEscape) => State::Normal,
_ => state,
};
escaped.push(ch);
}
escaped
}

View File

@ -1,6 +1,6 @@
[package]
name = "clippy_utils"
version = "0.1.74"
version = "0.1.75"
edition = "2021"
publish = false

View File

@ -9,11 +9,12 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
use rustc_lexer::tokenize;
use rustc_lint::LateContext;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::interpret::{alloc_range, Scalar};
use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, List, ScalarInt, Ty, TyCtxt};
use rustc_middle::{bug, mir, span_bug};
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::SyntaxContext;
use rustc_target::abi::Size;
use std::cmp::Ordering::{self, Equal};
use std::hash::{Hash, Hasher};
use std::iter;
@ -403,7 +404,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
&& adt_def.is_struct()
&& let Some(desired_field) = field_of_struct(*adt_def, self.lcx, *constant, field)
{
miri_to_const(self.lcx, desired_field)
mir_to_const(self.lcx, desired_field)
}
else {
result
@ -483,7 +484,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
.const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), None)
.ok()
.map(|val| rustc_middle::mir::Const::from_value(val, ty))?;
let result = miri_to_const(self.lcx, result)?;
let result = mir_to_const(self.lcx, result)?;
self.source = ConstantSource::Constant;
Some(result)
},
@ -655,10 +656,14 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
}
pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> {
pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> {
use rustc_middle::mir::ConstValue;
match result {
mir::Const::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() {
let mir::Const::Val(val, _) = result else {
// We only work on evaluated consts.
return None;
};
match (val, result.ty().kind()) {
(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() {
ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
@ -671,42 +676,28 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
_ => None,
},
mir::Const::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) =>
{
let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?;
(_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
let data = val.try_get_slice_bytes_for_diagnostics(lcx.tcx)?;
String::from_utf8(data.to_owned()).ok().map(Constant::Str)
},
mir::Const::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => {
let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory();
match result.ty().kind() {
ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
ty::Array(sub_type, len) => match sub_type.kind() {
ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) {
Some(len) => alloc
.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
.to_owned()
.array_chunks::<4>()
.map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
.collect::<Option<Vec<Constant<'tcx>>>>()
.map(Constant::Vec),
_ => None,
},
ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) {
Some(len) => alloc
.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
.to_owned()
.array_chunks::<8>()
.map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
.collect::<Option<Vec<Constant<'tcx>>>>()
.map(Constant::Vec),
_ => None,
},
_ => None,
},
_ => None,
(_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)),
(ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => {
let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
let len = len.try_to_target_usize(lcx.tcx)?;
let ty::Float(flt) = sub_type.kind() else {
return None;
};
let size = Size::from_bits(flt.bit_width());
let mut res = Vec::new();
for idx in 0..len {
let range = alloc_range(offset + size * idx, size);
let val = alloc.read_scalar(&lcx.tcx, range, /* read_provenance */ false).ok()?;
res.push(match flt {
FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)),
FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().ok()?)),
});
}
Some(Constant::Vec(res))
},
_ => None,
}

View File

@ -80,7 +80,6 @@ use std::sync::{Mutex, MutexGuard, OnceLock};
use if_chain::if_chain;
use itertools::Itertools;
use rustc_ast::ast::{self, LitKind, RangeLimits};
use rustc_ast::Attribute;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unhash::UnhashMap;
use rustc_hir::def::{DefKind, Res};
@ -113,7 +112,7 @@ use rustc_span::{sym, Span};
use rustc_target::abi::Integer;
use visitors::Visitable;
use crate::consts::{constant, miri_to_const, Constant};
use crate::consts::{constant, mir_to_const, Constant};
use crate::higher::Range;
use crate::ty::{
adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type,
@ -1509,9 +1508,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
if let rustc_ty::Adt(_, subst) = ty.kind()
&& let bnd_ty = subst.type_at(0)
&& let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx)
&& let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree()))
&& let min_const_kind = Const::from_value(const_val, bnd_ty)
&& let Some(min_const) = miri_to_const(cx, min_const_kind)
&& let Some(min_const) = mir_to_const(cx, Const::from_ty_const(min_val, cx.tcx))
&& let Some(start_const) = constant(cx, cx.typeck_results(), start)
{
start_const == min_const
@ -1525,9 +1522,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
if let rustc_ty::Adt(_, subst) = ty.kind()
&& let bnd_ty = subst.type_at(0)
&& let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx)
&& let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree()))
&& let max_const_kind = Const::from_value(const_val, bnd_ty)
&& let Some(max_const) = miri_to_const(cx, max_const_kind)
&& let Some(max_const) = mir_to_const(cx, Const::from_ty_const(max_val, cx.tcx))
&& let Some(end_const) = constant(cx, cx.typeck_results(), end)
{
end_const == max_const
@ -2456,11 +2451,12 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
})
}
/// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied
/// Checks if `id` has a `#[cfg(test)]` attribute applied
///
/// Note: Add `//@compile-flags: --test` to UI tests with a `#[cfg(test)]` function
pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
fn is_cfg_test(attr: &Attribute) -> bool {
/// This only checks directly applied attributes, to see if a node is inside a `#[cfg(test)]` parent
/// use [`is_in_cfg_test`]
pub fn is_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
tcx.hir().attrs(id).iter().any(|attr| {
if attr.has_name(sym::cfg)
&& let Some(items) = attr.meta_item_list()
&& let [item] = &*items
@ -2470,11 +2466,14 @@ pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
} else {
false
}
}
})
}
/// Checks if any parent node of `HirId` has `#[cfg(test)]` attribute applied
pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
tcx.hir()
.parent_iter(id)
.flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id))
.any(is_cfg_test)
.parent_id_iter(id)
.any(|parent_id| is_cfg_test(tcx, parent_id))
}
/// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied.

View File

@ -19,7 +19,7 @@ macro_rules! msrv_aliases {
// names may refer to stabilized feature flags or library items
msrv_aliases! {
1,71,0 { TUPLE_ARRAY_CONVERSIONS }
1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
1,70,0 { OPTION_IS_SOME_AND, BINARY_HEAP_RETAIN }
1,68,0 { PATH_MAIN_SEPARATOR_STR }
1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }

View File

@ -62,6 +62,27 @@ where
}
}
}
impl<'tcx, A, B> Visitable<'tcx> for (A, B)
where
A: Visitable<'tcx>,
B: Visitable<'tcx>,
{
fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
let (a, b) = self;
a.visit(visitor);
b.visit(visitor);
}
}
impl<'tcx, T> Visitable<'tcx> for Option<T>
where
T: Visitable<'tcx>,
{
fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
if let Some(x) = self {
x.visit(visitor);
}
}
}
macro_rules! visitable_ref {
($t:ident, $f:ident) => {
impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
@ -748,3 +769,26 @@ pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool {
})
.is_some()
}
/// If the local is only used once in `visitable` returns the path expression referencing the given
/// local
pub fn local_used_once<'tcx>(
cx: &LateContext<'tcx>,
visitable: impl Visitable<'tcx>,
id: HirId,
) -> Option<&'tcx Expr<'tcx>> {
let mut expr = None;
let cf = for_each_expr_with_closures(cx, visitable, |e| {
if path_to_local_id(e, id) && expr.replace(e).is_some() {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
});
if cf.is_some() {
return None;
}
expr
}

View File

@ -1,6 +1,6 @@
[package]
name = "declare_clippy_lint"
version = "0.1.74"
version = "0.1.75"
edition = "2021"
publish = false

View File

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2023-09-25"
channel = "nightly-2023-10-06"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]

View File

@ -26,6 +26,8 @@ use std::ops::Deref;
use std::path::Path;
use std::process::exit;
use anstream::println;
/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
fn arg_value<'a, T: Deref<Target = str>>(
@ -162,39 +164,15 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
}
}
#[allow(clippy::ignored_unit_patterns)]
fn display_help() {
println!(
"\
Checks a package to catch common mistakes and improve your Rust code.
Usage:
cargo clippy [options] [--] [<opts>...]
Common options:
-h, --help Print this message
--rustc Pass all args to rustc
-V, --version Print version info and exit
For the other options see `cargo check --help`.
To allow or deny a lint from the command line you can use `cargo clippy --`
with:
-W --warn OPT Set lint warnings
-A --allow OPT Set lint allowed
-D --deny OPT Set lint denied
-F --forbid OPT Set lint forbidden
You can use tool lints to allow or deny lints from your code, eg.:
#[allow(clippy::needless_lifetimes)]
"
);
println!("{}", help_message());
}
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml";
#[allow(clippy::too_many_lines)]
#[allow(clippy::ignored_unit_patterns)]
pub fn main() {
let handler = EarlyErrorHandler::new(ErrorOutputType::default());
@ -236,6 +214,7 @@ pub fn main() {
if orig_args.iter().any(|a| a == "--version" || a == "-V") {
let version_info = rustc_tools_util::get_version_info!();
println!("{version_info}");
exit(0);
}
@ -292,3 +271,25 @@ pub fn main() {
}
}))
}
#[must_use]
fn help_message() -> &'static str {
color_print::cstr!(
"Checks a file to catch common mistakes and improve your Rust code.
Run <cyan>clippy-driver</> with the same arguments you use for <cyan>rustc</>
<green,bold>Usage</>:
<cyan,bold>clippy-driver</> <cyan>[OPTIONS] INPUT</>
<green,bold>Common options:</>
<cyan,bold>-h</>, <cyan,bold>--help</> Print this message
<cyan,bold>-V</>, <cyan,bold>--version</> Print version info and exit
<cyan,bold>--rustc</> Pass all arguments to <cyan>rustc</>
<green,bold>Allowing / Denying lints</>
You can use tool lints to allow or deny lints from your code, e.g.:
<yellow,bold>#[allow(clippy::needless_lifetimes)]</>
"
)
}

View File

@ -6,37 +6,14 @@ use std::env;
use std::path::PathBuf;
use std::process::{self, Command};
const CARGO_CLIPPY_HELP: &str = "Checks a package to catch common mistakes and improve your Rust code.
Usage:
cargo clippy [options] [--] [<opts>...]
Common options:
--no-deps Run Clippy only on the given crate, without linting the dependencies
--fix Automatically apply lint suggestions. This flag implies `--no-deps` and `--all-targets`
-h, --help Print this message
-V, --version Print version info and exit
--explain LINT Print the documentation for a given lint
For the other options see `cargo check --help`.
To allow or deny a lint from the command line you can use `cargo clippy --`
with:
-W --warn OPT Set lint warnings
-A --allow OPT Set lint allowed
-D --deny OPT Set lint denied
-F --forbid OPT Set lint forbidden
You can use tool lints to allow or deny lints from your code, e.g.:
#[allow(clippy::needless_lifetimes)]
";
use anstream::println;
#[allow(clippy::ignored_unit_patterns)]
fn show_help() {
println!("{CARGO_CLIPPY_HELP}");
println!("{}", help_message());
}
#[allow(clippy::ignored_unit_patterns)]
fn show_version() {
let version_info = rustc_tools_util::get_version_info!();
println!("{version_info}");
@ -168,6 +145,38 @@ where
}
}
#[must_use]
pub fn help_message() -> &'static str {
color_print::cstr!(
"Checks a package to catch common mistakes and improve your Rust code.
<green,bold>Usage</>:
<cyan,bold>cargo clippy</> <cyan>[OPTIONS] [--] [<<ARGS>>...]</>
<green,bold>Common options:</>
<cyan,bold>--no-deps</> Run Clippy only on the given crate, without linting the dependencies
<cyan,bold>--fix</> Automatically apply lint suggestions. This flag implies <cyan>--no-deps</> and <cyan>--all-targets</>
<cyan,bold>-h</>, <cyan,bold>--help</> Print this message
<cyan,bold>-V</>, <cyan,bold>--version</> Print version info and exit
<cyan,bold>--explain [LINT]</> Print the documentation for a given lint
See all options with <cyan,bold>cargo check --help</>.
<green,bold>Allowing / Denying lints</>
To allow or deny a lint from the command line you can use <cyan,bold>cargo clippy --</> with:
<cyan,bold>-W</> / <cyan,bold>--warn</> <cyan>[LINT]</> Set lint warnings
<cyan,bold>-A</> / <cyan,bold>--allow</> <cyan>[LINT]</> Set lint allowed
<cyan,bold>-D</> / <cyan,bold>--deny</> <cyan>[LINT]</> Set lint denied
<cyan,bold>-F</> / <cyan,bold>--forbid</> <cyan>[LINT]</> Set lint forbidden
You can use tool lints to allow or deny lints from your code, e.g.:
<yellow,bold>#[allow(clippy::needless_lifetimes)]</>
"
)
}
#[cfg(test)]
mod tests {
use super::ClippyCmd;

View File

@ -21,6 +21,18 @@ pub fn derive(_: TokenStream) -> TokenStream {
output
}
#[proc_macro_derive(ImplStructWithStdDisplay)]
pub fn derive_std(_: TokenStream) -> TokenStream {
quote! {
struct A {}
impl ::std::fmt::Display for A {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "A")
}
}
}
}
#[proc_macro_derive(FieldReassignWithDefault)]
pub fn derive_foo(_input: TokenStream) -> TokenStream {
quote! {
@ -141,3 +153,19 @@ pub fn shadow_derive(_: TokenStream) -> TokenStream {
.into(),
])
}
#[proc_macro_derive(StructIgnoredUnitPattern)]
pub fn derive_ignored_unit_pattern(_: TokenStream) -> TokenStream {
quote! {
struct A;
impl A {
fn a(&self) -> Result<(), ()> {
unimplemented!()
}
pub fn b(&self) {
let _ = self.a().unwrap();
}
}
}
}

View File

@ -1,3 +1,4 @@
//@aux-build:proc_macro_derive.rs
#![warn(clippy::ignored_unit_patterns)]
#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)]
@ -14,8 +15,22 @@ fn main() {
//~^ ERROR: matching over `()` is more explicit
let _ = foo().map_err(|()| todo!());
//~^ ERROR: matching over `()` is more explicit
println!(
"{:?}",
match foo() {
Ok(()) => {},
//~^ ERROR: matching over `()` is more explicit
Err(()) => {},
//~^ ERROR: matching over `()` is more explicit
}
);
}
// ignored_unit_patterns in derive macro should be ok
#[derive(proc_macro_derive::StructIgnoredUnitPattern)]
pub struct B;
#[allow(unused)]
pub fn moo(_: ()) {
let () = foo().unwrap();

View File

@ -1,3 +1,4 @@
//@aux-build:proc_macro_derive.rs
#![warn(clippy::ignored_unit_patterns)]
#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)]
@ -14,8 +15,22 @@ fn main() {
//~^ ERROR: matching over `()` is more explicit
let _ = foo().map_err(|_| todo!());
//~^ ERROR: matching over `()` is more explicit
println!(
"{:?}",
match foo() {
Ok(_) => {},
//~^ ERROR: matching over `()` is more explicit
Err(_) => {},
//~^ ERROR: matching over `()` is more explicit
}
);
}
// ignored_unit_patterns in derive macro should be ok
#[derive(proc_macro_derive::StructIgnoredUnitPattern)]
pub struct B;
#[allow(unused)]
pub fn moo(_: ()) {
let _ = foo().unwrap();

View File

@ -1,5 +1,5 @@
error: matching over `()` is more explicit
--> $DIR/ignored_unit_patterns.rs:10:12
--> $DIR/ignored_unit_patterns.rs:11:12
|
LL | Ok(_) => {},
| ^ help: use `()` instead of `_`: `()`
@ -8,28 +8,40 @@ LL | Ok(_) => {},
= help: to override `-D warnings` add `#[allow(clippy::ignored_unit_patterns)]`
error: matching over `()` is more explicit
--> $DIR/ignored_unit_patterns.rs:11:13
--> $DIR/ignored_unit_patterns.rs:12:13
|
LL | Err(_) => {},
| ^ help: use `()` instead of `_`: `()`
error: matching over `()` is more explicit
--> $DIR/ignored_unit_patterns.rs:13:15
--> $DIR/ignored_unit_patterns.rs:14:15
|
LL | if let Ok(_) = foo() {}
| ^ help: use `()` instead of `_`: `()`
error: matching over `()` is more explicit
--> $DIR/ignored_unit_patterns.rs:15:28
--> $DIR/ignored_unit_patterns.rs:16:28
|
LL | let _ = foo().map_err(|_| todo!());
| ^ help: use `()` instead of `_`: `()`
error: matching over `()` is more explicit
--> $DIR/ignored_unit_patterns.rs:21:9
--> $DIR/ignored_unit_patterns.rs:22:16
|
LL | Ok(_) => {},
| ^ help: use `()` instead of `_`: `()`
error: matching over `()` is more explicit
--> $DIR/ignored_unit_patterns.rs:24:17
|
LL | Err(_) => {},
| ^ help: use `()` instead of `_`: `()`
error: matching over `()` is more explicit
--> $DIR/ignored_unit_patterns.rs:36:9
|
LL | let _ = foo().unwrap();
| ^ help: use `()` instead of `_`: `()`
error: aborting due to 5 previous errors
error: aborting due to 7 previous errors

View File

@ -7,8 +7,6 @@ fn fn_constref(i: &i32) -> i32 {
unimplemented!()
}
fn fn_mutref(i: &mut i32) {
//~^ ERROR: this argument is a mutable reference, but not used mutably
//~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
unimplemented!()
}
fn fooi() -> i32 {

View File

@ -1,5 +1,5 @@
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:24:11
--> $DIR/infinite_loop.rs:22:11
|
LL | while y < 10 {
| ^^^^^^
@ -8,7 +8,7 @@ LL | while y < 10 {
= note: `#[deny(clippy::while_immutable_condition)]` on by default
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:31:11
--> $DIR/infinite_loop.rs:29:11
|
LL | while y < 10 && x < 3 {
| ^^^^^^^^^^^^^^^
@ -16,7 +16,7 @@ LL | while y < 10 && x < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:40:11
--> $DIR/infinite_loop.rs:38:11
|
LL | while !cond {
| ^^^^^
@ -24,7 +24,7 @@ LL | while !cond {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:86:11
--> $DIR/infinite_loop.rs:84:11
|
LL | while i < 3 {
| ^^^^^
@ -32,7 +32,7 @@ LL | while i < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:93:11
--> $DIR/infinite_loop.rs:91:11
|
LL | while i < 3 && j > 0 {
| ^^^^^^^^^^^^^^
@ -40,7 +40,7 @@ LL | while i < 3 && j > 0 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:99:11
--> $DIR/infinite_loop.rs:97:11
|
LL | while i < 3 {
| ^^^^^
@ -48,7 +48,7 @@ LL | while i < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:116:11
--> $DIR/infinite_loop.rs:114:11
|
LL | while i < 3 {
| ^^^^^
@ -56,7 +56,7 @@ LL | while i < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:123:11
--> $DIR/infinite_loop.rs:121:11
|
LL | while i < 3 {
| ^^^^^
@ -64,7 +64,7 @@ LL | while i < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:191:15
--> $DIR/infinite_loop.rs:189:15
|
LL | while self.count < n {
| ^^^^^^^^^^^^^^
@ -72,7 +72,7 @@ LL | while self.count < n {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:201:11
--> $DIR/infinite_loop.rs:199:11
|
LL | while y < 10 {
| ^^^^^^
@ -82,7 +82,7 @@ LL | while y < 10 {
= help: rewrite it as `if cond { loop { } }`
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:210:11
--> $DIR/infinite_loop.rs:208:11
|
LL | while y < 10 {
| ^^^^^^
@ -91,14 +91,5 @@ LL | while y < 10 {
= note: this loop contains `return`s or `break`s
= help: rewrite it as `if cond { loop { } }`
error: this argument is a mutable reference, but not used mutably
--> $DIR/infinite_loop.rs:9:17
|
LL | fn fn_mutref(i: &mut i32) {
| ^^^^^^^^ help: consider changing to: `&i32`
|
= note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]`
error: aborting due to 12 previous errors
error: aborting due to 11 previous errors

View File

@ -0,0 +1,124 @@
//@no-rustfix
#![warn(clippy::into_iter_without_iter)]
use std::iter::IntoIterator;
fn main() {
{
struct S;
impl<'a> IntoIterator for &'a S {
//~^ ERROR: `IntoIterator` implemented for a reference type without an `iter` method
type IntoIter = std::slice::Iter<'a, u8>;
type Item = &'a u8;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
impl<'a> IntoIterator for &'a mut S {
//~^ ERROR: `IntoIterator` implemented for a reference type without an `iter_mut` method
type IntoIter = std::slice::IterMut<'a, u8>;
type Item = &'a mut u8;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
}
{
struct S<T>(T);
impl<'a, T> IntoIterator for &'a S<T> {
//~^ ERROR: `IntoIterator` implemented for a reference type without an `iter` method
type IntoIter = std::slice::Iter<'a, T>;
type Item = &'a T;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
impl<'a, T> IntoIterator for &'a mut S<T> {
//~^ ERROR: `IntoIterator` implemented for a reference type without an `iter_mut` method
type IntoIter = std::slice::IterMut<'a, T>;
type Item = &'a mut T;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
}
{
// Both iter and iter_mut methods exist, don't lint
struct S<'a, T>(&'a T);
impl<'a, T> S<'a, T> {
fn iter(&self) -> std::slice::Iter<'a, T> {
todo!()
}
fn iter_mut(&mut self) -> std::slice::IterMut<'a, T> {
todo!()
}
}
impl<'a, T> IntoIterator for &S<'a, T> {
type IntoIter = std::slice::Iter<'a, T>;
type Item = &'a T;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
impl<'a, T> IntoIterator for &mut S<'a, T> {
type IntoIter = std::slice::IterMut<'a, T>;
type Item = &'a mut T;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
}
{
// Only `iter` exists, no `iter_mut`
struct S<'a, T>(&'a T);
impl<'a, T> S<'a, T> {
fn iter(&self) -> std::slice::Iter<'a, T> {
todo!()
}
}
impl<'a, T> IntoIterator for &S<'a, T> {
type IntoIter = std::slice::Iter<'a, T>;
type Item = &'a T;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
impl<'a, T> IntoIterator for &mut S<'a, T> {
//~^ ERROR: `IntoIterator` implemented for a reference type without an `iter_mut` method
type IntoIter = std::slice::IterMut<'a, T>;
type Item = &'a mut T;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
}
{
// `iter` exists, but `IntoIterator` is implemented for an alias. inherent_impls doesn't "normalize"
// aliases so that `inherent_impls(Alias)` where `type Alias = S` returns nothing, so this can lead
// to fun FPs. Make sure it doesn't happen here (we're using type_of, which should skip the alias).
struct S;
impl S {
fn iter(&self) -> std::slice::Iter<'static, u8> {
todo!()
}
}
type Alias = S;
impl IntoIterator for &Alias {
type IntoIter = std::slice::Iter<'static, u8>;
type Item = &'static u8;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
}
}

View File

@ -0,0 +1,114 @@
error: `IntoIterator` implemented for a reference type without an `iter` method
--> $DIR/into_iter_without_iter.rs:10:9
|
LL | / impl<'a> IntoIterator for &'a S {
LL | |
LL | | type IntoIter = std::slice::Iter<'a, u8>;
LL | | type Item = &'a u8;
... |
LL | | }
LL | | }
| |_________^
|
= note: `-D clippy::into-iter-without-iter` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::into_iter_without_iter)]`
help: consider implementing `iter`
|
LL ~
LL + impl S {
LL + fn iter(&self) -> std::slice::Iter<'a, u8> {
LL + <&Self as IntoIterator>::into_iter(self)
LL + }
LL + }
|
error: `IntoIterator` implemented for a reference type without an `iter_mut` method
--> $DIR/into_iter_without_iter.rs:18:9
|
LL | / impl<'a> IntoIterator for &'a mut S {
LL | |
LL | | type IntoIter = std::slice::IterMut<'a, u8>;
LL | | type Item = &'a mut u8;
... |
LL | | }
LL | | }
| |_________^
|
help: consider implementing `iter_mut`
|
LL ~
LL + impl S {
LL + fn iter_mut(&mut self) -> std::slice::IterMut<'a, u8> {
LL + <&mut Self as IntoIterator>::into_iter(self)
LL + }
LL + }
|
error: `IntoIterator` implemented for a reference type without an `iter` method
--> $DIR/into_iter_without_iter.rs:29:9
|
LL | / impl<'a, T> IntoIterator for &'a S<T> {
LL | |
LL | | type IntoIter = std::slice::Iter<'a, T>;
LL | | type Item = &'a T;
... |
LL | | }
LL | | }
| |_________^
|
help: consider implementing `iter`
|
LL ~
LL + impl S<T> {
LL + fn iter(&self) -> std::slice::Iter<'a, T> {
LL + <&Self as IntoIterator>::into_iter(self)
LL + }
LL + }
|
error: `IntoIterator` implemented for a reference type without an `iter_mut` method
--> $DIR/into_iter_without_iter.rs:37:9
|
LL | / impl<'a, T> IntoIterator for &'a mut S<T> {
LL | |
LL | | type IntoIter = std::slice::IterMut<'a, T>;
LL | | type Item = &'a mut T;
... |
LL | | }
LL | | }
| |_________^
|
help: consider implementing `iter_mut`
|
LL ~
LL + impl S<T> {
LL + fn iter_mut(&mut self) -> std::slice::IterMut<'a, T> {
LL + <&mut Self as IntoIterator>::into_iter(self)
LL + }
LL + }
|
error: `IntoIterator` implemented for a reference type without an `iter_mut` method
--> $DIR/into_iter_without_iter.rs:93:9
|
LL | / impl<'a, T> IntoIterator for &mut S<'a, T> {
LL | |
LL | | type IntoIter = std::slice::IterMut<'a, T>;
LL | | type Item = &'a mut T;
... |
LL | | }
LL | | }
| |_________^
|
help: consider implementing `iter_mut`
|
LL ~
LL + impl S<'a, T> {
LL + fn iter_mut(&mut self) -> std::slice::IterMut<'a, T> {
LL + <&mut Self as IntoIterator>::into_iter(self)
LL + }
LL + }
|
error: aborting due to 5 previous errors

View File

@ -0,0 +1,11 @@
//@aux-build:../auxiliary/proc_macros.rs
extern crate proc_macros;
proc_macros::with_span! {
span
#[cfg(test)]
mod tests {}
}
#[test]
fn f() {}

View File

@ -0,0 +1,4 @@
#[cfg(test)]
mod tests {}
fn in_submodule() {}

View File

@ -1,2 +0,0 @@
error: Option 'test' given more than once

View File

@ -0,0 +1,8 @@
#[path = "auxiliary/submodule.rs"]
mod submodule;
#[cfg(test)]
mod tests {
#[test]
fn t() {}
}

View File

@ -0,0 +1,14 @@
error: items after a test module
--> $DIR/auxiliary/submodule.rs:2:1
|
LL | mod tests {}
| ^^^^^^^^^
LL |
LL | fn in_submodule() {}
| ^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::items-after-test-module` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::items_after_test_module)]`
error: aborting due to previous error

View File

@ -0,0 +1,11 @@
#[cfg(test)]
mod tests {
#[test]
fn f() {}
}
#[cfg(test)]
mod more_tests {
#[test]
fn g() {}
}

View File

@ -1,4 +1,3 @@
//@compile-flags: --test
#![allow(unused)]
#![warn(clippy::items_after_test_module)]
@ -6,6 +5,13 @@ fn main() {}
fn should_not_lint() {}
fn should_lint() {}
const SHOULD_ALSO_LINT: usize = 1;
macro_rules! should_lint {
() => {};
}
#[allow(dead_code)]
#[allow(unused)] // Some attributes to check that span replacement is good enough
#[allow(clippy::allow_attributes)]
@ -14,10 +20,3 @@ mod tests {
#[test]
fn hi() {}
}
fn should_lint() {}
const SHOULD_ALSO_LINT: usize = 1;
macro_rules! should_not_lint {
() => {};
}

View File

@ -0,0 +1,22 @@
#![allow(unused)]
#![warn(clippy::items_after_test_module)]
fn main() {}
fn should_not_lint() {}
#[allow(dead_code)]
#[allow(unused)] // Some attributes to check that span replacement is good enough
#[allow(clippy::allow_attributes)]
#[cfg(test)]
mod tests {
#[test]
fn hi() {}
}
fn should_lint() {}
const SHOULD_ALSO_LINT: usize = 1;
macro_rules! should_lint {
() => {};
}

View File

@ -0,0 +1,20 @@
error: items after a test module
--> $DIR/root_module.rs:12:1
|
LL | mod tests {
| ^^^^^^^^^
...
LL | fn should_lint() {}
| ^^^^^^^^^^^^^^^^
LL |
LL | const SHOULD_ALSO_LINT: usize = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | macro_rules! should_lint {
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::items-after-test-module` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::items_after_test_module)]`
= help: move the items to before the test module was defined
error: aborting due to previous error

View File

@ -0,0 +1,120 @@
//@no-rustfix
#![warn(clippy::iter_without_into_iter)]
fn main() {
{
struct S;
impl S {
pub fn iter(&self) -> std::slice::Iter<'_, u8> {
//~^ ERROR: `iter` method without an `IntoIterator` impl
[].iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> {
//~^ ERROR: `iter_mut` method without an `IntoIterator` impl
[].iter_mut()
}
}
}
{
struct S;
impl S {
pub fn iter(&self) -> impl Iterator<Item = &u8> {
// RPITIT is not stable, so we can't generally suggest it here yet
[].iter()
}
}
}
{
struct S<'a>(&'a mut [u8]);
impl<'a> S<'a> {
pub fn iter(&self) -> std::slice::Iter<'_, u8> {
//~^ ERROR: `iter` method without an `IntoIterator` impl
self.0.iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> {
//~^ ERROR: `iter_mut` method without an `IntoIterator` impl
self.0.iter_mut()
}
}
}
{
// Incompatible signatures
struct S;
impl S {
pub fn iter(self) -> std::slice::Iter<'static, u8> {
todo!()
}
}
struct S2;
impl S2 {
pub async fn iter(&self) -> std::slice::Iter<'static, u8> {
todo!()
}
}
struct S3;
impl S3 {
pub fn iter(&self, _additional_param: ()) -> std::slice::Iter<'static, u8> {
todo!()
}
}
struct S4<T>(T);
impl<T> S4<T> {
pub fn iter<U>(&self) -> std::slice::Iter<'static, (T, U)> {
todo!()
}
}
struct S5<T>(T);
impl<T> S5<T> {
pub fn iter(&self) -> std::slice::Iter<'static, T> {
todo!()
}
}
}
{
struct S<T>(T);
impl<T> S<T> {
pub fn iter(&self) -> std::slice::Iter<'_, T> {
//~^ ERROR: `iter` method without an `IntoIterator` impl
todo!()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
//~^ ERROR: `iter_mut` method without an `IntoIterator` impl
todo!()
}
}
}
{
struct S<T>(T);
impl<T> S<T> {
pub fn iter(&self) -> std::slice::Iter<'_, T> {
// Don't lint, there's an existing (wrong) IntoIterator impl
todo!()
}
}
impl<'a, T> IntoIterator for &'a S<T> {
type Item = &'a String;
type IntoIter = std::slice::Iter<'a, String>;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
}
{
struct S<T>(T);
impl<T> S<T> {
pub fn iter_mut(&self) -> std::slice::IterMut<'_, T> {
// Don't lint, there's an existing (wrong) IntoIterator impl
todo!()
}
}
impl<'a, T> IntoIterator for &'a mut S<T> {
type Item = &'a mut String;
type IntoIter = std::slice::IterMut<'a, String>;
fn into_iter(self) -> Self::IntoIter {
todo!()
}
}
}
}

View File

@ -0,0 +1,150 @@
error: `iter` method without an `IntoIterator` impl for `&S`
--> $DIR/iter_without_into_iter.rs:8:13
|
LL | / pub fn iter(&self) -> std::slice::Iter<'_, u8> {
LL | |
LL | | [].iter()
LL | | }
| |_____________^
|
= note: `-D clippy::iter-without-into-iter` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::iter_without_into_iter)]`
help: consider implementing `IntoIterator` for `&S`
|
LL ~
LL + impl IntoIterator for &S {
LL + type IntoIter = std::slice::Iter<'_, u8>;
LL + type Iter = &u8;
LL + fn into_iter() -> Self::IntoIter {
LL + self.iter()
LL + }
LL + }
|
error: `iter_mut` method without an `IntoIterator` impl for `&mut S`
--> $DIR/iter_without_into_iter.rs:12:13
|
LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> {
LL | |
LL | | [].iter_mut()
LL | | }
| |_____________^
|
help: consider implementing `IntoIterator` for `&mut S`
|
LL ~
LL + impl IntoIterator for &mut S {
LL + type IntoIter = std::slice::IterMut<'_, u8>;
LL + type Iter = &mut u8;
LL + fn into_iter() -> Self::IntoIter {
LL + self.iter()
LL + }
LL + }
|
error: `iter` method without an `IntoIterator` impl for `&S<'a>`
--> $DIR/iter_without_into_iter.rs:30:13
|
LL | / pub fn iter(&self) -> std::slice::Iter<'_, u8> {
LL | |
LL | | self.0.iter()
LL | | }
| |_____________^
|
help: consider implementing `IntoIterator` for `&S<'a>`
|
LL ~
LL + impl IntoIterator for &S<'a> {
LL + type IntoIter = std::slice::Iter<'_, u8>;
LL + type Iter = &u8;
LL + fn into_iter() -> Self::IntoIter {
LL + self.iter()
LL + }
LL + }
|
error: `iter_mut` method without an `IntoIterator` impl for `&mut S<'a>`
--> $DIR/iter_without_into_iter.rs:34:13
|
LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> {
LL | |
LL | | self.0.iter_mut()
LL | | }
| |_____________^
|
help: consider implementing `IntoIterator` for `&mut S<'a>`
|
LL ~
LL + impl IntoIterator for &mut S<'a> {
LL + type IntoIter = std::slice::IterMut<'_, u8>;
LL + type Iter = &mut u8;
LL + fn into_iter() -> Self::IntoIter {
LL + self.iter()
LL + }
LL + }
|
error: `iter` method without an `IntoIterator` impl for `&S5<T>`
--> $DIR/iter_without_into_iter.rs:68:13
|
LL | / pub fn iter(&self) -> std::slice::Iter<'static, T> {
LL | | todo!()
LL | | }
| |_____________^
|
help: consider implementing `IntoIterator` for `&S5<T>`
|
LL ~
LL + impl IntoIterator for &S5<T> {
LL + type IntoIter = std::slice::Iter<'static, T>;
LL + type Iter = &T;
LL + fn into_iter() -> Self::IntoIter {
LL + self.iter()
LL + }
LL + }
|
error: `iter` method without an `IntoIterator` impl for `&S<T>`
--> $DIR/iter_without_into_iter.rs:76:13
|
LL | / pub fn iter(&self) -> std::slice::Iter<'_, T> {
LL | |
LL | | todo!()
LL | | }
| |_____________^
|
help: consider implementing `IntoIterator` for `&S<T>`
|
LL ~
LL + impl IntoIterator for &S<T> {
LL + type IntoIter = std::slice::Iter<'_, T>;
LL + type Iter = &T;
LL + fn into_iter() -> Self::IntoIter {
LL + self.iter()
LL + }
LL + }
|
error: `iter_mut` method without an `IntoIterator` impl for `&mut S<T>`
--> $DIR/iter_without_into_iter.rs:80:13
|
LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
LL | |
LL | | todo!()
LL | | }
| |_____________^
|
help: consider implementing `IntoIterator` for `&mut S<T>`
|
LL ~
LL + impl IntoIterator for &mut S<T> {
LL + type IntoIter = std::slice::IterMut<'_, T>;
LL + type Iter = &mut T;
LL + fn into_iter() -> Self::IntoIter {
LL + self.iter()
LL + }
LL + }
|
error: aborting due to 7 previous errors

View File

@ -9,8 +9,6 @@ fn custom() -> impl Future<Output = ()> {
}
fn do_something_to_future(future: &mut impl Future<Output = ()>) {}
//~^ ERROR: this argument is a mutable reference, but not used mutably
//~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
fn main() {
let _ = some_async_fn();

View File

@ -1,5 +1,5 @@
error: non-binding `let` on a future
--> $DIR/let_underscore_future.rs:16:5
--> $DIR/let_underscore_future.rs:14:5
|
LL | let _ = some_async_fn();
| ^^^^^^^^^^^^^^^^^^^^^^^^
@ -9,7 +9,7 @@ LL | let _ = some_async_fn();
= help: to override `-D warnings` add `#[allow(clippy::let_underscore_future)]`
error: non-binding `let` on a future
--> $DIR/let_underscore_future.rs:18:5
--> $DIR/let_underscore_future.rs:16:5
|
LL | let _ = custom();
| ^^^^^^^^^^^^^^^^^
@ -17,21 +17,12 @@ LL | let _ = custom();
= help: consider awaiting the future or dropping explicitly with `std::mem::drop`
error: non-binding `let` on a future
--> $DIR/let_underscore_future.rs:23:5
--> $DIR/let_underscore_future.rs:21:5
|
LL | let _ = future;
| ^^^^^^^^^^^^^^^
|
= help: consider awaiting the future or dropping explicitly with `std::mem::drop`
error: this argument is a mutable reference, but not used mutably
--> $DIR/let_underscore_future.rs:11:35
|
LL | fn do_something_to_future(future: &mut impl Future<Output = ()>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&impl Future<Output = ()>`
|
= note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]`
error: aborting due to 4 previous errors
error: aborting due to 3 previous errors

View File

@ -0,0 +1,89 @@
#![warn(clippy::manual_hash_one)]
#![allow(clippy::needless_borrows_for_generic_args)]
use std::hash::{BuildHasher, Hash, Hasher};
fn returned(b: impl BuildHasher) -> u64 {
b.hash_one(&true)
}
fn unsized_receiver(b: impl BuildHasher, s: &str) {
let _ = b.hash_one(&s[4..10]);
}
fn owned_value(b: impl BuildHasher, v: Vec<u32>) -> Vec<u32> {
let _ = b.hash_one(&v);
v
}
fn reused_hasher(b: impl BuildHasher) {
let mut hasher = b.build_hasher();
true.hash(&mut hasher);
let _ = hasher.finish();
let _ = hasher.finish();
}
fn reused_hasher_in_return(b: impl BuildHasher) -> u64 {
let mut hasher = b.build_hasher();
true.hash(&mut hasher);
let _ = hasher.finish();
hasher.finish()
}
fn no_hash(b: impl BuildHasher) {
let mut hasher = b.build_hasher();
let _ = hasher.finish();
}
fn hash_twice(b: impl BuildHasher) {
let mut hasher = b.build_hasher();
true.hash(&mut hasher);
true.hash(&mut hasher);
let _ = hasher.finish();
}
fn other_hasher(b: impl BuildHasher) {
let mut other_hasher = b.build_hasher();
let mut hasher = b.build_hasher();
true.hash(&mut other_hasher);
let _ = hasher.finish();
}
fn finish_then_hash(b: impl BuildHasher) {
let mut hasher = b.build_hasher();
let _ = hasher.finish();
true.hash(&mut hasher);
}
fn in_macro(b: impl BuildHasher) {
macro_rules! m {
($b:expr) => {{
let mut hasher = $b.build_hasher();
true.hash(&mut hasher);
let _ = hasher.finish();
}};
}
m!(b);
}
#[clippy::msrv = "1.70"]
fn msrv_1_70(b: impl BuildHasher, v: impl Hash) {
let mut hasher = b.build_hasher();
v.hash(&mut hasher);
let _ = hasher.finish();
}
#[clippy::msrv = "1.71"]
fn msrv_1_71(b: impl BuildHasher, v: impl Hash) {
let _ = b.hash_one(&v);
}

View File

@ -0,0 +1,89 @@
#![warn(clippy::manual_hash_one)]
#![allow(clippy::needless_borrows_for_generic_args)]
use std::hash::{BuildHasher, Hash, Hasher};
fn returned(b: impl BuildHasher) -> u64 {
let mut hasher = b.build_hasher();
true.hash(&mut hasher);
hasher.finish()
}
fn unsized_receiver(b: impl BuildHasher, s: &str) {
let mut hasher = b.build_hasher();
s[4..10].hash(&mut hasher);
let _ = hasher.finish();
}
fn owned_value(b: impl BuildHasher, v: Vec<u32>) -> Vec<u32> {
let mut hasher = b.build_hasher();
v.hash(&mut hasher);
let _ = hasher.finish();
v
}
fn reused_hasher(b: impl BuildHasher) {
let mut hasher = b.build_hasher();
true.hash(&mut hasher);
let _ = hasher.finish();
let _ = hasher.finish();
}
fn reused_hasher_in_return(b: impl BuildHasher) -> u64 {
let mut hasher = b.build_hasher();
true.hash(&mut hasher);
let _ = hasher.finish();
hasher.finish()
}
fn no_hash(b: impl BuildHasher) {
let mut hasher = b.build_hasher();
let _ = hasher.finish();
}
fn hash_twice(b: impl BuildHasher) {
let mut hasher = b.build_hasher();
true.hash(&mut hasher);
true.hash(&mut hasher);
let _ = hasher.finish();
}
fn other_hasher(b: impl BuildHasher) {
let mut other_hasher = b.build_hasher();
let mut hasher = b.build_hasher();
true.hash(&mut other_hasher);
let _ = hasher.finish();
}
fn finish_then_hash(b: impl BuildHasher) {
let mut hasher = b.build_hasher();
let _ = hasher.finish();
true.hash(&mut hasher);
}
fn in_macro(b: impl BuildHasher) {
macro_rules! m {
($b:expr) => {{
let mut hasher = $b.build_hasher();
true.hash(&mut hasher);
let _ = hasher.finish();
}};
}
m!(b);
}
#[clippy::msrv = "1.70"]
fn msrv_1_70(b: impl BuildHasher, v: impl Hash) {
let mut hasher = b.build_hasher();
v.hash(&mut hasher);
let _ = hasher.finish();
}
#[clippy::msrv = "1.71"]
fn msrv_1_71(b: impl BuildHasher, v: impl Hash) {
let mut hasher = b.build_hasher();
v.hash(&mut hasher);
let _ = hasher.finish();
}

View File

@ -0,0 +1,56 @@
error: manual implementation of `BuildHasher::hash_one`
--> $DIR/manual_hash_one.rs:9:5
|
LL | hasher.finish()
| ^^^^^^^^^^^^^^^
|
= note: `-D clippy::manual-hash-one` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::manual_hash_one)]`
help: try
|
LL ~
LL ~
LL ~ b.hash_one(&true)
|
error: manual implementation of `BuildHasher::hash_one`
--> $DIR/manual_hash_one.rs:15:13
|
LL | let _ = hasher.finish();
| ^^^^^^^^^^^^^^^
|
help: try
|
LL ~
LL ~
LL ~ let _ = b.hash_one(&s[4..10]);
|
error: manual implementation of `BuildHasher::hash_one`
--> $DIR/manual_hash_one.rs:21:13
|
LL | let _ = hasher.finish();
| ^^^^^^^^^^^^^^^
|
help: try
|
LL ~
LL ~
LL ~ let _ = b.hash_one(&v);
|
error: manual implementation of `BuildHasher::hash_one`
--> $DIR/manual_hash_one.rs:88:13
|
LL | let _ = hasher.finish();
| ^^^^^^^^^^^^^^^
|
help: try
|
LL ~
LL ~
LL ~ let _ = b.hash_one(&v);
|
error: aborting due to 4 previous errors

View File

@ -133,3 +133,7 @@ fn not_fire() {
[data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ ..] => data,
};
}
fn issue11579() {
let Some(msg) = Some("hi") else { unreachable!("can't happen") };
}

View File

@ -170,3 +170,11 @@ fn not_fire() {
[data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ ..] => data,
};
}
fn issue11579() {
let msg = match Some("hi") {
//~^ ERROR: this could be rewritten as `let...else`
Some(m) => m,
_ => unreachable!("can't happen"),
};
}

View File

@ -92,5 +92,15 @@ LL | | _ => return,
LL | | };
| |______^ help: consider writing: `let ([data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ .., 0]) = data.as_slice() else { return };`
error: aborting due to 9 previous errors
error: this could be rewritten as `let...else`
--> $DIR/manual_let_else_match.rs:175:5
|
LL | / let msg = match Some("hi") {
LL | |
LL | | Some(m) => m,
LL | | _ => unreachable!("can't happen"),
LL | | };
| |______^ help: consider writing: `let Some(msg) = Some("hi") else { unreachable!("can't happen") };`
error: aborting due to 10 previous errors

View File

@ -10,10 +10,9 @@ enum E {
_C,
}
// user forgot to remove the marker
// if the user explicitly marks as nonexhaustive we shouldn't warn them
#[non_exhaustive]
enum Ep {
//~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern
A,
B,
#[doc(hidden)]

View File

@ -22,23 +22,5 @@ LL | _C,
= note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::manual_non_exhaustive)]`
error: this seems like a manual implementation of the non-exhaustive pattern
--> $DIR/manual_non_exhaustive_enum.rs:15:1
|
LL | / enum Ep {
LL | |
LL | | A,
LL | | B,
LL | | #[doc(hidden)]
LL | | _C,
LL | | }
| |_^
|
help: remove this variant
--> $DIR/manual_non_exhaustive_enum.rs:20:5
|
LL | _C,
| ^^
error: aborting due to 2 previous errors
error: aborting due to previous error

View File

@ -32,8 +32,6 @@ fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<K
//~^ ERROR: mutable key type
//~| NOTE: `-D clippy::mutable-key-type` implied by `-D warnings`
//~| ERROR: mutable key type
//~| ERROR: this argument is a mutable reference, but not used mutably
//~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
let _other: HashMap<Key, bool> = HashMap::new();
//~^ ERROR: mutable key type
m.keys().cloned().collect()

View File

@ -14,103 +14,94 @@ LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> Hash
| ^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:37:5
--> $DIR/mut_key.rs:35:5
|
LL | let _other: HashMap<Key, bool> = HashMap::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:65:22
--> $DIR/mut_key.rs:63:22
|
LL | fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:78:5
--> $DIR/mut_key.rs:76:5
|
LL | let _map = HashMap::<Cell<usize>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:80:5
--> $DIR/mut_key.rs:78:5
|
LL | let _map = HashMap::<&mut Cell<usize>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:82:5
--> $DIR/mut_key.rs:80:5
|
LL | let _map = HashMap::<&mut usize, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:85:5
--> $DIR/mut_key.rs:83:5
|
LL | let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:87:5
--> $DIR/mut_key.rs:85:5
|
LL | let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:89:5
--> $DIR/mut_key.rs:87:5
|
LL | let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:91:5
--> $DIR/mut_key.rs:89:5
|
LL | let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:93:5
--> $DIR/mut_key.rs:91:5
|
LL | let _map = HashMap::<Option<Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:95:5
--> $DIR/mut_key.rs:93:5
|
LL | let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:97:5
--> $DIR/mut_key.rs:95:5
|
LL | let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:100:5
--> $DIR/mut_key.rs:98:5
|
LL | let _map = HashMap::<Box<Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:102:5
--> $DIR/mut_key.rs:100:5
|
LL | let _map = HashMap::<Rc<Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> $DIR/mut_key.rs:104:5
--> $DIR/mut_key.rs:102:5
|
LL | let _map = HashMap::<Arc<Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this argument is a mutable reference, but not used mutably
--> $DIR/mut_key.rs:31:32
|
LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&HashMap<Key, usize>`
|
= note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]`
error: aborting due to 18 previous errors
error: aborting due to 17 previous errors

View File

@ -22,8 +22,6 @@ impl MyStruct {
fn takes_an_immutable_reference(&self, a: &i32) {}
fn takes_a_mutable_reference(&self, a: &mut i32) {}
//~^ ERROR: this argument is a mutable reference, but not used mutably
//~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
}
#[warn(clippy::unnecessary_mut_passed)]

View File

@ -1,5 +1,5 @@
error: the function `takes_an_immutable_reference` doesn't need a mutable reference
--> $DIR/mut_reference.rs:32:34
--> $DIR/mut_reference.rs:30:34
|
LL | takes_an_immutable_reference(&mut 42);
| ^^^^^^^
@ -8,25 +8,16 @@ LL | takes_an_immutable_reference(&mut 42);
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]`
error: the function `as_ptr` doesn't need a mutable reference
--> $DIR/mut_reference.rs:36:12
--> $DIR/mut_reference.rs:34:12
|
LL | as_ptr(&mut 42);
| ^^^^^^^
error: the method `takes_an_immutable_reference` doesn't need a mutable reference
--> $DIR/mut_reference.rs:41:44
--> $DIR/mut_reference.rs:39:44
|
LL | my_struct.takes_an_immutable_reference(&mut 42);
| ^^^^^^^
error: this argument is a mutable reference, but not used mutably
--> $DIR/mut_reference.rs:24:44
|
LL | fn takes_a_mutable_reference(&self, a: &mut i32) {}
| ^^^^^^^^ help: consider changing to: `&i32`
|
= note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]`
error: aborting due to 4 previous errors
error: aborting due to 3 previous errors

View File

@ -1,4 +1,5 @@
#![allow(clippy::if_same_then_else, clippy::no_effect, clippy::redundant_closure_call)]
#![warn(clippy::needless_pass_by_ref_mut)]
#![feature(lint_reasons)]
//@no-rustfix
use std::ptr::NonNull;

View File

@ -1,5 +1,5 @@
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:6:11
--> $DIR/needless_pass_by_ref_mut.rs:7:11
|
LL | fn foo(s: &mut Vec<u32>, b: &u32, x: &mut u32) {
| ^^^^^^^^^^^^^ help: consider changing to: `&Vec<u32>`
@ -8,79 +8,79 @@ LL | fn foo(s: &mut Vec<u32>, b: &u32, x: &mut u32) {
= help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:31:12
--> $DIR/needless_pass_by_ref_mut.rs:32:12
|
LL | fn foo6(s: &mut Vec<u32>) {
| ^^^^^^^^^^^^^ help: consider changing to: `&Vec<u32>`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:44:29
--> $DIR/needless_pass_by_ref_mut.rs:45:29
|
LL | fn mushroom(&self, vec: &mut Vec<i32>) -> usize {
| ^^^^^^^^^^^^^ help: consider changing to: `&Vec<i32>`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:49:31
--> $DIR/needless_pass_by_ref_mut.rs:50:31
|
LL | fn badger(&mut self, vec: &mut Vec<i32>) -> usize {
| ^^^^^^^^^^^^^ help: consider changing to: `&Vec<i32>`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:126:16
--> $DIR/needless_pass_by_ref_mut.rs:127:16
|
LL | async fn a1(x: &mut i32) {
| ^^^^^^^^ help: consider changing to: `&i32`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:130:16
--> $DIR/needless_pass_by_ref_mut.rs:131:16
|
LL | async fn a2(x: &mut i32, y: String) {
| ^^^^^^^^ help: consider changing to: `&i32`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:134:16
--> $DIR/needless_pass_by_ref_mut.rs:135:16
|
LL | async fn a3(x: &mut i32, y: String, z: String) {
| ^^^^^^^^ help: consider changing to: `&i32`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:138:16
--> $DIR/needless_pass_by_ref_mut.rs:139:16
|
LL | async fn a4(x: &mut i32, y: i32) {
| ^^^^^^^^ help: consider changing to: `&i32`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:142:24
--> $DIR/needless_pass_by_ref_mut.rs:143:24
|
LL | async fn a5(x: i32, y: &mut i32) {
| ^^^^^^^^ help: consider changing to: `&i32`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:146:24
--> $DIR/needless_pass_by_ref_mut.rs:147:24
|
LL | async fn a6(x: i32, y: &mut i32) {
| ^^^^^^^^ help: consider changing to: `&i32`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:150:32
--> $DIR/needless_pass_by_ref_mut.rs:151:32
|
LL | async fn a7(x: i32, y: i32, z: &mut i32) {
| ^^^^^^^^ help: consider changing to: `&i32`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:154:24
--> $DIR/needless_pass_by_ref_mut.rs:155:24
|
LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) {
| ^^^^^^^^ help: consider changing to: `&i32`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:154:45
--> $DIR/needless_pass_by_ref_mut.rs:155:45
|
LL | async fn a8(x: i32, a: &mut i32, y: i32, z: &mut i32) {
| ^^^^^^^^ help: consider changing to: `&i32`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:188:16
--> $DIR/needless_pass_by_ref_mut.rs:189:16
|
LL | fn cfg_warn(s: &mut u32) {}
| ^^^^^^^^ help: consider changing to: `&u32`
@ -88,7 +88,7 @@ LL | fn cfg_warn(s: &mut u32) {}
= note: this is cfg-gated and may require further changes
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:194:20
--> $DIR/needless_pass_by_ref_mut.rs:195:20
|
LL | fn cfg_warn(s: &mut u32) {}
| ^^^^^^^^ help: consider changing to: `&u32`
@ -96,19 +96,19 @@ LL | fn cfg_warn(s: &mut u32) {}
= note: this is cfg-gated and may require further changes
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:208:39
--> $DIR/needless_pass_by_ref_mut.rs:209:39
|
LL | async fn inner_async2(x: &mut i32, y: &mut u32) {
| ^^^^^^^^ help: consider changing to: `&u32`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:216:26
--> $DIR/needless_pass_by_ref_mut.rs:217:26
|
LL | async fn inner_async3(x: &mut i32, y: &mut u32) {
| ^^^^^^^^ help: consider changing to: `&i32`
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:235:34
--> $DIR/needless_pass_by_ref_mut.rs:236:34
|
LL | pub async fn call_in_closure1(n: &mut str) {
| ^^^^^^^^ help: consider changing to: `&str`
@ -116,7 +116,7 @@ LL | pub async fn call_in_closure1(n: &mut str) {
= warning: changing this function will impact semver compatibility
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:247:25
--> $DIR/needless_pass_by_ref_mut.rs:248:25
|
LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() {
| ^^^^^^^^^^ help: consider changing to: `&usize`
@ -124,7 +124,7 @@ LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() {
= warning: changing this function will impact semver compatibility
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:254:20
--> $DIR/needless_pass_by_ref_mut.rs:255:20
|
LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize {
| ^^^^^^^^^^ help: consider changing to: `&usize`
@ -132,7 +132,7 @@ LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize {
= warning: changing this function will impact semver compatibility
error: this argument is a mutable reference, but not used mutably
--> $DIR/needless_pass_by_ref_mut.rs:265:26
--> $DIR/needless_pass_by_ref_mut.rs:266:26
|
LL | pub async fn closure4(n: &mut usize) {
| ^^^^^^^^^^ help: consider changing to: `&usize`

View File

@ -18,4 +18,8 @@ fn main() {
multiline
string
";
"no hashes";
b"no hashes";
c"no hashes";
}

View File

@ -18,4 +18,8 @@ fn main() {
multiline
string
"#;
r"no hashes";
br"no hashes";
cr"no hashes";
}

View File

@ -6,7 +6,7 @@ LL | r#"aaa"#;
|
= note: `-D clippy::needless-raw-strings` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]`
help: try
help: use a plain string literal instead
|
LL - r#"aaa"#;
LL + "aaa";
@ -18,7 +18,7 @@ error: unnecessary raw string literal
LL | br#"aaa"#;
| ^^^^^^^^^
|
help: try
help: use a plain byte string literal instead
|
LL - br#"aaa"#;
LL + b"aaa";
@ -30,7 +30,7 @@ error: unnecessary raw string literal
LL | cr#"aaa"#;
| ^^^^^^^^^
|
help: try
help: use a plain C string literal instead
|
LL - cr#"aaa"#;
LL + c"aaa";
@ -46,7 +46,7 @@ LL | | string
LL | | "#;
| |______^
|
help: try
help: use a plain string literal instead
|
LL ~ "
LL | a
@ -55,5 +55,41 @@ LL | string
LL ~ ";
|
error: aborting due to 4 previous errors
error: unnecessary raw string literal
--> $DIR/needless_raw_string.rs:22:5
|
LL | r"no hashes";
| ^^^^^^^^^^^^
|
help: use a plain string literal instead
|
LL - r"no hashes";
LL + "no hashes";
|
error: unnecessary raw string literal
--> $DIR/needless_raw_string.rs:23:5
|
LL | br"no hashes";
| ^^^^^^^^^^^^^
|
help: use a plain byte string literal instead
|
LL - br"no hashes";
LL + b"no hashes";
|
error: unnecessary raw string literal
--> $DIR/needless_raw_string.rs:24:5
|
LL | cr"no hashes";
| ^^^^^^^^^^^^^
|
help: use a plain C string literal instead
|
LL - cr"no hashes";
LL + c"no hashes";
|
error: aborting due to 7 previous errors

View File

@ -6,7 +6,7 @@ LL | r#"\aaa"#;
|
= note: `-D clippy::needless-raw-string-hashes` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::needless_raw_string_hashes)]`
help: remove all the hashes around the literal
help: remove all the hashes around the string literal
|
LL - r#"\aaa"#;
LL + r"\aaa";
@ -18,7 +18,7 @@ error: unnecessary hashes around raw string literal
LL | r##"Hello "world"!"##;
| ^^^^^^^^^^^^^^^^^^^^^
|
help: remove one hash from both sides of the literal
help: remove one hash from both sides of the string literal
|
LL - r##"Hello "world"!"##;
LL + r#"Hello "world"!"#;
@ -30,7 +30,7 @@ error: unnecessary hashes around raw string literal
LL | r######" "### "## "# "######;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove 2 hashes from both sides of the literal
help: remove 2 hashes from both sides of the string literal
|
LL - r######" "### "## "# "######;
LL + r####" "### "## "# "####;
@ -42,7 +42,7 @@ error: unnecessary hashes around raw string literal
LL | r######" "aa" "# "## "######;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove 3 hashes from both sides of the literal
help: remove 3 hashes from both sides of the string literal
|
LL - r######" "aa" "# "## "######;
LL + r###" "aa" "# "## "###;
@ -54,7 +54,7 @@ error: unnecessary hashes around raw string literal
LL | br#"\aaa"#;
| ^^^^^^^^^^
|
help: remove all the hashes around the literal
help: remove all the hashes around the byte string literal
|
LL - br#"\aaa"#;
LL + br"\aaa";
@ -66,7 +66,7 @@ error: unnecessary hashes around raw string literal
LL | br##"Hello "world"!"##;
| ^^^^^^^^^^^^^^^^^^^^^^
|
help: remove one hash from both sides of the literal
help: remove one hash from both sides of the byte string literal
|
LL - br##"Hello "world"!"##;
LL + br#"Hello "world"!"#;
@ -78,7 +78,7 @@ error: unnecessary hashes around raw string literal
LL | br######" "### "## "# "######;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove 2 hashes from both sides of the literal
help: remove 2 hashes from both sides of the byte string literal
|
LL - br######" "### "## "# "######;
LL + br####" "### "## "# "####;
@ -90,7 +90,7 @@ error: unnecessary hashes around raw string literal
LL | br######" "aa" "# "## "######;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove 3 hashes from both sides of the literal
help: remove 3 hashes from both sides of the byte string literal
|
LL - br######" "aa" "# "## "######;
LL + br###" "aa" "# "## "###;
@ -102,7 +102,7 @@ error: unnecessary hashes around raw string literal
LL | cr#"\aaa"#;
| ^^^^^^^^^^
|
help: remove all the hashes around the literal
help: remove all the hashes around the C string literal
|
LL - cr#"\aaa"#;
LL + cr"\aaa";
@ -114,7 +114,7 @@ error: unnecessary hashes around raw string literal
LL | cr##"Hello "world"!"##;
| ^^^^^^^^^^^^^^^^^^^^^^
|
help: remove one hash from both sides of the literal
help: remove one hash from both sides of the C string literal
|
LL - cr##"Hello "world"!"##;
LL + cr#"Hello "world"!"#;
@ -126,7 +126,7 @@ error: unnecessary hashes around raw string literal
LL | cr######" "### "## "# "######;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove 2 hashes from both sides of the literal
help: remove 2 hashes from both sides of the C string literal
|
LL - cr######" "### "## "# "######;
LL + cr####" "### "## "# "####;
@ -138,7 +138,7 @@ error: unnecessary hashes around raw string literal
LL | cr######" "aa" "# "## "######;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove 3 hashes from both sides of the literal
help: remove 3 hashes from both sides of the C string literal
|
LL - cr######" "aa" "# "## "######;
LL + cr###" "aa" "# "## "###;
@ -154,7 +154,7 @@ LL | | string
LL | | "#;
| |______^
|
help: remove all the hashes around the literal
help: remove all the hashes around the string literal
|
LL ~ r"
LL | \a
@ -169,7 +169,7 @@ error: unnecessary hashes around raw string literal
LL | r###"rust"###;
| ^^^^^^^^^^^^^
|
help: remove all the hashes around the literal
help: remove all the hashes around the string literal
|
LL - r###"rust"###;
LL + r"rust";
@ -181,7 +181,7 @@ error: unnecessary hashes around raw string literal
LL | r#"hello world"#;
| ^^^^^^^^^^^^^^^^
|
help: remove all the hashes around the literal
help: remove all the hashes around the string literal
|
LL - r#"hello world"#;
LL + r"hello world";

View File

@ -39,20 +39,30 @@ fn main() {
// throw a warning
println!("hello world");
//~^ ERROR: literal with an empty format string
//~| ERROR: literal with an empty format string
println!("world hello");
//~^ ERROR: literal with an empty format string
//~| ERROR: literal with an empty format string
// named args shouldn't change anything either
println!("hello world");
//~^ ERROR: literal with an empty format string
//~| ERROR: literal with an empty format string
println!("world hello");
//~^ ERROR: literal with an empty format string
//~| ERROR: literal with an empty format string
// The string literal from `file!()` has a callsite span that isn't marked as coming from an
// expansion
println!("file: {}", file!());
// Braces in unicode escapes should not be escaped
println!("{{}} \x00 \u{ab123} \\\u{ab123} {{:?}}");
println!("\\\u{1234}");
// This does not lint because it would have to suggest unescaping the character
println!(r"{}", "\u{ab123}");
// These are not unicode escapes
println!("\\u{{ab123}} \\u{{{{");
println!(r"\u{{ab123}} \u{{{{");
println!("\\{{ab123}} \\u{{{{");
println!("\\u{{ab123}}");
println!("\\\\u{{1234}}");
println!("mixed: {{hello}} {world}");
}

View File

@ -39,20 +39,30 @@ fn main() {
// throw a warning
println!("{0} {1}", "hello", "world");
//~^ ERROR: literal with an empty format string
//~| ERROR: literal with an empty format string
println!("{1} {0}", "hello", "world");
//~^ ERROR: literal with an empty format string
//~| ERROR: literal with an empty format string
// named args shouldn't change anything either
println!("{foo} {bar}", foo = "hello", bar = "world");
//~^ ERROR: literal with an empty format string
//~| ERROR: literal with an empty format string
println!("{bar} {foo}", foo = "hello", bar = "world");
//~^ ERROR: literal with an empty format string
//~| ERROR: literal with an empty format string
// The string literal from `file!()` has a callsite span that isn't marked as coming from an
// expansion
println!("file: {}", file!());
// Braces in unicode escapes should not be escaped
println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}");
println!("{}", "\\\u{1234}");
// This does not lint because it would have to suggest unescaping the character
println!(r"{}", "\u{ab123}");
// These are not unicode escapes
println!("{}", r"\u{ab123} \u{{");
println!(r"{}", r"\u{ab123} \u{{");
println!("{}", r"\{ab123} \u{{");
println!("{}", "\\u{ab123}");
println!("{}", "\\\\u{1234}");
println!("mixed: {} {world}", "{hello}");
}

View File

@ -52,97 +52,145 @@ error: literal with an empty format string
--> $DIR/print_literal.rs:40:25
|
LL | println!("{0} {1}", "hello", "world");
| ^^^^^^^
| ^^^^^^^^^^^^^^^^
|
help: try
|
LL - println!("{0} {1}", "hello", "world");
LL + println!("hello {1}", "world");
LL + println!("hello world");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:40:34
|
LL | println!("{0} {1}", "hello", "world");
| ^^^^^^^
|
help: try
|
LL - println!("{0} {1}", "hello", "world");
LL + println!("{0} world", "hello");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:43:34
--> $DIR/print_literal.rs:42:25
|
LL | println!("{1} {0}", "hello", "world");
| ^^^^^^^
| ^^^^^^^^^^^^^^^^
|
help: try
|
LL - println!("{1} {0}", "hello", "world");
LL + println!("world {0}", "hello");
LL + println!("world hello");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:43:25
--> $DIR/print_literal.rs:46:35
|
LL | println!("{1} {0}", "hello", "world");
| ^^^^^^^
LL | println!("{foo} {bar}", foo = "hello", bar = "world");
| ^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - println!("{1} {0}", "hello", "world");
LL + println!("{1} hello", "world");
LL - println!("{foo} {bar}", foo = "hello", bar = "world");
LL + println!("hello world");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:48:35
|
LL | println!("{foo} {bar}", foo = "hello", bar = "world");
| ^^^^^^^
|
help: try
|
LL - println!("{foo} {bar}", foo = "hello", bar = "world");
LL + println!("hello {bar}", bar = "world");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:48:50
|
LL | println!("{foo} {bar}", foo = "hello", bar = "world");
| ^^^^^^^
|
help: try
|
LL - println!("{foo} {bar}", foo = "hello", bar = "world");
LL + println!("{foo} world", foo = "hello");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:51:50
|
LL | println!("{bar} {foo}", foo = "hello", bar = "world");
| ^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - println!("{bar} {foo}", foo = "hello", bar = "world");
LL + println!("world {foo}", foo = "hello");
LL + println!("world hello");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:51:35
--> $DIR/print_literal.rs:56:20
|
LL | println!("{bar} {foo}", foo = "hello", bar = "world");
| ^^^^^^^
LL | println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - println!("{bar} {foo}", foo = "hello", bar = "world");
LL + println!("{bar} hello", bar = "world");
LL - println!("{}", "{} \x00 \u{ab123} \\\u{ab123} {:?}");
LL + println!("{{}} \x00 \u{ab123} \\\u{ab123} {{:?}}");
|
error: aborting due to 12 previous errors
error: literal with an empty format string
--> $DIR/print_literal.rs:57:20
|
LL | println!("{}", "\\\u{1234}");
| ^^^^^^^^^^^^
|
help: try
|
LL - println!("{}", "\\\u{1234}");
LL + println!("\\\u{1234}");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:61:20
|
LL | println!("{}", r"\u{ab123} \u{{");
| ^^^^^^^^^^^^^^^^^
|
help: try
|
LL - println!("{}", r"\u{ab123} \u{{");
LL + println!("\\u{{ab123}} \\u{{{{");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:62:21
|
LL | println!(r"{}", r"\u{ab123} \u{{");
| ^^^^^^^^^^^^^^^^^
|
help: try
|
LL - println!(r"{}", r"\u{ab123} \u{{");
LL + println!(r"\u{{ab123}} \u{{{{");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:63:20
|
LL | println!("{}", r"\{ab123} \u{{");
| ^^^^^^^^^^^^^^^^
|
help: try
|
LL - println!("{}", r"\{ab123} \u{{");
LL + println!("\\{{ab123}} \\u{{{{");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:64:20
|
LL | println!("{}", "\\u{ab123}");
| ^^^^^^^^^^^^
|
help: try
|
LL - println!("{}", "\\u{ab123}");
LL + println!("\\u{{ab123}}");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:65:20
|
LL | println!("{}", "\\\\u{1234}");
| ^^^^^^^^^^^^^
|
help: try
|
LL - println!("{}", "\\\\u{1234}");
LL + println!("\\\\u{{1234}}");
|
error: literal with an empty format string
--> $DIR/print_literal.rs:67:35
|
LL | println!("mixed: {} {world}", "{hello}");
| ^^^^^^^^^
|
help: try
|
LL - println!("mixed: {} {world}", "{hello}");
LL + println!("mixed: {{hello}} {world}");
|
error: aborting due to 16 previous errors

View File

@ -118,3 +118,40 @@ fn macros() {
let x = x;
}
}
struct WithDrop(usize);
impl Drop for WithDrop {
fn drop(&mut self) {}
}
struct InnerDrop(WithDrop);
struct ComposeDrop {
d: WithDrop,
}
struct WithoutDrop(usize);
fn drop_trait() {
let a = WithDrop(1);
let b = WithDrop(2);
let a = a;
}
fn without_drop() {
let a = WithoutDrop(1);
let b = WithoutDrop(2);
let a = a;
}
fn drop_inner() {
let a = InnerDrop(WithDrop(1));
let b = InnerDrop(WithDrop(2));
let a = a;
}
fn drop_compose() {
let a = ComposeDrop { d: WithDrop(1) };
let b = ComposeDrop { d: WithDrop(1) };
let a = a;
}

View File

@ -1,137 +1,172 @@
error: redundant redefinition of a binding
error: redundant redefinition of a binding `x`
--> $DIR/redundant_locals.rs:12:5
|
LL | let x = x;
| ^^^^^^^^^^
|
help: `x` is initially defined here
--> $DIR/redundant_locals.rs:11:9
|
LL | let x = 1;
| ^
LL | let x = x;
| ^^^^^^^^^^
|
= help: remove the redefinition of `x`
= note: `-D clippy::redundant-locals` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::redundant_locals)]`
error: redundant redefinition of a binding
error: redundant redefinition of a binding `x`
--> $DIR/redundant_locals.rs:17:5
|
LL | let mut x = x;
| ^^^^^^^^^^^^^^
|
help: `x` is initially defined here
--> $DIR/redundant_locals.rs:16:9
|
LL | let mut x = 1;
| ^^^^^
LL | let mut x = x;
| ^^^^^^^^^^^^^^
|
= help: remove the redefinition of `x`
error: redundant redefinition of a binding
error: redundant redefinition of a binding `x`
--> $DIR/redundant_locals.rs:47:5
|
LL | let x = x;
| ^^^^^^^^^^
|
help: `x` is initially defined here
--> $DIR/redundant_locals.rs:46:14
|
LL | fn parameter(x: i32) {
| ^
error: redundant redefinition of a binding `x`
--> $DIR/redundant_locals.rs:52:5
|
LL | let x = x;
| ^^^^^^^^^^
|
= help: remove the redefinition of `x`
error: redundant redefinition of a binding
help: `x` is initially defined here
--> $DIR/redundant_locals.rs:51:9
|
LL | let x = 1;
| ^
error: redundant redefinition of a binding `x`
--> $DIR/redundant_locals.rs:53:5
|
LL | let x = x;
| ^^^^^^^^^^
|
= help: remove the redefinition of `x`
error: redundant redefinition of a binding
help: `x` is initially defined here
--> $DIR/redundant_locals.rs:52:9
|
LL | let x = x;
| ^
error: redundant redefinition of a binding `x`
--> $DIR/redundant_locals.rs:54:5
|
LL | let x = x;
| ^^^^^^^^^^
|
= help: remove the redefinition of `x`
error: redundant redefinition of a binding
help: `x` is initially defined here
--> $DIR/redundant_locals.rs:53:9
|
LL | let x = x;
| ^
error: redundant redefinition of a binding `x`
--> $DIR/redundant_locals.rs:55:5
|
LL | let x = x;
| ^^^^^^^^^^
|
= help: remove the redefinition of `x`
error: redundant redefinition of a binding
help: `x` is initially defined here
--> $DIR/redundant_locals.rs:54:9
|
LL | let x = x;
| ^
LL | let x = x;
error: redundant redefinition of a binding `a`
--> $DIR/redundant_locals.rs:61:5
|
LL | let a = a;
| ^^^^^^^^^^
|
= help: remove the redefinition of `x`
error: redundant redefinition of a binding
help: `a` is initially defined here
--> $DIR/redundant_locals.rs:59:9
|
LL | let a = 1;
| ^
LL | let b = 2;
LL | let a = a;
error: redundant redefinition of a binding `b`
--> $DIR/redundant_locals.rs:62:5
|
LL | let b = b;
| ^^^^^^^^^^
|
= help: remove the redefinition of `a`
error: redundant redefinition of a binding
help: `b` is initially defined here
--> $DIR/redundant_locals.rs:60:9
|
LL | let b = 2;
| ^
LL | let a = a;
LL | let b = b;
| ^^^^^^^^^^
|
= help: remove the redefinition of `b`
error: redundant redefinition of a binding
error: redundant redefinition of a binding `x`
--> $DIR/redundant_locals.rs:68:9
|
LL | let x = x;
| ^^^^^^^^^^
|
help: `x` is initially defined here
--> $DIR/redundant_locals.rs:67:13
|
LL | let x = 1;
| ^
error: redundant redefinition of a binding `x`
--> $DIR/redundant_locals.rs:75:9
|
LL | let x = x;
| ^^^^^^^^^^
|
= help: remove the redefinition of `x`
error: redundant redefinition of a binding
help: `x` is initially defined here
--> $DIR/redundant_locals.rs:74:13
|
LL | let x = 1;
| ^
error: redundant redefinition of a binding `x`
--> $DIR/redundant_locals.rs:78:9
|
LL | let x = x;
| ^^^^^^^^^^
|
= help: remove the redefinition of `x`
error: redundant redefinition of a binding
help: `x` is initially defined here
--> $DIR/redundant_locals.rs:77:6
|
LL | |x: i32| {
| ^
error: redundant redefinition of a binding `x`
--> $DIR/redundant_locals.rs:97:9
|
LL | let x = x;
| ^^^^^^^^^^
|
= help: remove the redefinition of `x`
error: redundant redefinition of a binding
help: `x` is initially defined here
--> $DIR/redundant_locals.rs:94:9
|
LL | let x = 1;
| ^
...
LL | let x = x;
| ^^^^^^^^^^
error: redundant redefinition of a binding `a`
--> $DIR/redundant_locals.rs:144:5
|
= help: remove the redefinition of `x`
LL | let a = a;
| ^^^^^^^^^^
|
help: `a` is initially defined here
--> $DIR/redundant_locals.rs:142:9
|
LL | let a = WithoutDrop(1);
| ^
error: aborting due to 13 previous errors
error: aborting due to 14 previous errors

View File

@ -40,8 +40,6 @@ impl T {
pub fn hash(&self, state: &mut T) {
//~^ ERROR: method `hash` can be confused for the standard trait method `std::hash::Ha
//~| ERROR: this argument is a mutable reference, but not used mutably
//~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
unimplemented!()
}

View File

@ -38,8 +38,6 @@ error: method `hash` can be confused for the standard trait method `std::hash::H
|
LL | / pub fn hash(&self, state: &mut T) {
LL | |
LL | |
LL | |
LL | | unimplemented!()
LL | | }
| |_____^
@ -47,7 +45,7 @@ LL | | }
= help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name
error: method `index` can be confused for the standard trait method `std::ops::Index::index`
--> $DIR/method_list_2.rs:48:5
--> $DIR/method_list_2.rs:46:5
|
LL | / pub fn index(&self, index: usize) -> &Self {
LL | |
@ -58,7 +56,7 @@ LL | | }
= help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name
error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut`
--> $DIR/method_list_2.rs:53:5
--> $DIR/method_list_2.rs:51:5
|
LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self {
LL | |
@ -69,7 +67,7 @@ LL | | }
= help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name
error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter`
--> $DIR/method_list_2.rs:58:5
--> $DIR/method_list_2.rs:56:5
|
LL | / pub fn into_iter(self) -> Self {
LL | |
@ -80,7 +78,7 @@ LL | | }
= help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name
error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul`
--> $DIR/method_list_2.rs:63:5
--> $DIR/method_list_2.rs:61:5
|
LL | / pub fn mul(self, rhs: Self) -> Self {
LL | |
@ -91,7 +89,7 @@ LL | | }
= help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name
error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg`
--> $DIR/method_list_2.rs:68:5
--> $DIR/method_list_2.rs:66:5
|
LL | / pub fn neg(self) -> Self {
LL | |
@ -102,7 +100,7 @@ LL | | }
= help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name
error: method `next` can be confused for the standard trait method `std::iter::Iterator::next`
--> $DIR/method_list_2.rs:73:5
--> $DIR/method_list_2.rs:71:5
|
LL | / pub fn next(&mut self) -> Option<Self> {
LL | |
@ -113,7 +111,7 @@ LL | | }
= help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name
error: method `not` can be confused for the standard trait method `std::ops::Not::not`
--> $DIR/method_list_2.rs:78:5
--> $DIR/method_list_2.rs:76:5
|
LL | / pub fn not(self) -> Self {
LL | |
@ -124,7 +122,7 @@ LL | | }
= help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name
error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem`
--> $DIR/method_list_2.rs:83:5
--> $DIR/method_list_2.rs:81:5
|
LL | / pub fn rem(self, rhs: Self) -> Self {
LL | |
@ -135,7 +133,7 @@ LL | | }
= help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name
error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl`
--> $DIR/method_list_2.rs:88:5
--> $DIR/method_list_2.rs:86:5
|
LL | / pub fn shl(self, rhs: Self) -> Self {
LL | |
@ -146,7 +144,7 @@ LL | | }
= help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name
error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr`
--> $DIR/method_list_2.rs:93:5
--> $DIR/method_list_2.rs:91:5
|
LL | / pub fn shr(self, rhs: Self) -> Self {
LL | |
@ -157,7 +155,7 @@ LL | | }
= help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name
error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub`
--> $DIR/method_list_2.rs:98:5
--> $DIR/method_list_2.rs:96:5
|
LL | / pub fn sub(self, rhs: Self) -> Self {
LL | |
@ -167,15 +165,5 @@ LL | | }
|
= help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name
error: this argument is a mutable reference, but not used mutably
--> $DIR/method_list_2.rs:41:31
|
LL | pub fn hash(&self, state: &mut T) {
| ^^^^^^ help: consider changing to: `&T`
|
= warning: changing this function will impact semver compatibility
= note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]`
error: aborting due to 16 previous errors
error: aborting due to 15 previous errors

View File

@ -103,8 +103,6 @@ fn from_empty_vec() {
}
fn do_stuff(vec: &mut [u8]) {}
//~^ ERROR: this argument is a mutable reference, but not used mutably
//~| NOTE: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
fn extend_vector_with_manipulations_between() {
let len = 300;

View File

@ -105,14 +105,5 @@ LL | vec1 = vec![];
LL | vec1.resize(10, 0);
| ^^^^^^^^^^^^^^^^^^
error: this argument is a mutable reference, but not used mutably
--> $DIR/slow_vector_initialization.rs:105:18
|
LL | fn do_stuff(vec: &mut [u8]) {}
| ^^^^^^^^^ help: consider changing to: `&[u8]`
|
= note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]`
error: aborting due to 14 previous errors
error: aborting due to 13 previous errors

View File

@ -1,8 +1,12 @@
//@aux-build:proc_macro_derive.rs
#![warn(clippy::std_instead_of_core)]
#![allow(unused_imports)]
extern crate alloc;
#[macro_use]
extern crate proc_macro_derive;
#[warn(clippy::std_instead_of_core)]
fn std_instead_of_core() {
// Regular import
@ -55,6 +59,13 @@ fn alloc_instead_of_core() {
//~^ ERROR: used import from `alloc` instead of `core`
}
mod std_in_proc_macro_derive {
#[warn(clippy::alloc_instead_of_core)]
#[allow(unused)]
#[derive(ImplStructWithStdDisplay)]
struct B {}
}
fn main() {
std_instead_of_core();
std_instead_of_alloc();

View File

@ -1,8 +1,12 @@
//@aux-build:proc_macro_derive.rs
#![warn(clippy::std_instead_of_core)]
#![allow(unused_imports)]
extern crate alloc;
#[macro_use]
extern crate proc_macro_derive;
#[warn(clippy::std_instead_of_core)]
fn std_instead_of_core() {
// Regular import
@ -55,6 +59,13 @@ fn alloc_instead_of_core() {
//~^ ERROR: used import from `alloc` instead of `core`
}
mod std_in_proc_macro_derive {
#[warn(clippy::alloc_instead_of_core)]
#[allow(unused)]
#[derive(ImplStructWithStdDisplay)]
struct B {}
}
fn main() {
std_instead_of_core();
std_instead_of_alloc();

View File

@ -1,5 +1,5 @@
error: used import from `std` instead of `core`
--> $DIR/std_instead_of_core.rs:9:9
--> $DIR/std_instead_of_core.rs:13:9
|
LL | use std::hash::Hasher;
| ^^^ help: consider importing the item from `core`: `core`
@ -8,49 +8,49 @@ LL | use std::hash::Hasher;
= help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]`
error: used import from `std` instead of `core`
--> $DIR/std_instead_of_core.rs:12:11
--> $DIR/std_instead_of_core.rs:16:11
|
LL | use ::std::hash::Hash;
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `core`
--> $DIR/std_instead_of_core.rs:18:9
--> $DIR/std_instead_of_core.rs:22:9
|
LL | use std::fmt::{Debug, Result};
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `core`
--> $DIR/std_instead_of_core.rs:22:15
--> $DIR/std_instead_of_core.rs:26:15
|
LL | let ptr = std::ptr::null::<u32>();
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `core`
--> $DIR/std_instead_of_core.rs:24:21
--> $DIR/std_instead_of_core.rs:28:21
|
LL | let ptr_mut = ::std::ptr::null_mut::<usize>();
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `core`
--> $DIR/std_instead_of_core.rs:28:16
--> $DIR/std_instead_of_core.rs:32:16
|
LL | let cell = std::cell::Cell::new(8u32);
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `core`
--> $DIR/std_instead_of_core.rs:30:27
--> $DIR/std_instead_of_core.rs:34:27
|
LL | let cell_absolute = ::std::cell::Cell::new(8u32);
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `core`
--> $DIR/std_instead_of_core.rs:39:9
--> $DIR/std_instead_of_core.rs:43:9
|
LL | use std::iter::Iterator;
| ^^^ help: consider importing the item from `core`: `core`
error: used import from `std` instead of `alloc`
--> $DIR/std_instead_of_core.rs:46:9
--> $DIR/std_instead_of_core.rs:50:9
|
LL | use std::vec;
| ^^^ help: consider importing the item from `alloc`: `alloc`
@ -59,13 +59,13 @@ LL | use std::vec;
= help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]`
error: used import from `std` instead of `alloc`
--> $DIR/std_instead_of_core.rs:48:9
--> $DIR/std_instead_of_core.rs:52:9
|
LL | use std::vec::Vec;
| ^^^ help: consider importing the item from `alloc`: `alloc`
error: used import from `alloc` instead of `core`
--> $DIR/std_instead_of_core.rs:54:9
--> $DIR/std_instead_of_core.rs:58:9
|
LL | use alloc::slice::from_ref;
| ^^^^^ help: consider importing the item from `core`: `core`

View File

@ -69,6 +69,34 @@ mod struct_mod {
}
}
// issue 9942
mod underscore_mod {
// allow use of `deref` so that `clippy --fix` includes `Deref`.
#![allow(noop_method_call)]
mod exports_underscore {
pub use std::ops::Deref as _;
pub fn dummy() {}
}
mod exports_underscore_ish {
pub use std::ops::Deref as _Deref;
pub fn dummy() {}
}
fn does_not_lint() {
use self::exports_underscore::*;
let _ = (&0).deref();
dummy();
}
fn does_lint() {
use self::exports_underscore_ish::{_Deref, dummy};
let _ = (&0).deref();
dummy();
}
}
fn main() {
foo();
multi_foo();

View File

@ -69,6 +69,34 @@ mod struct_mod {
}
}
// issue 9942
mod underscore_mod {
// allow use of `deref` so that `clippy --fix` includes `Deref`.
#![allow(noop_method_call)]
mod exports_underscore {
pub use std::ops::Deref as _;
pub fn dummy() {}
}
mod exports_underscore_ish {
pub use std::ops::Deref as _Deref;
pub fn dummy() {}
}
fn does_not_lint() {
use self::exports_underscore::*;
let _ = (&0).deref();
dummy();
}
fn does_lint() {
use self::exports_underscore_ish::*;
let _ = (&0).deref();
dummy();
}
}
fn main() {
foo();
multi_foo();

View File

@ -38,55 +38,61 @@ LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:97:13
--> $DIR/wildcard_imports.rs:94:13
|
LL | use self::exports_underscore_ish::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `self::exports_underscore_ish::{_Deref, dummy}`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:125:13
|
LL | use crate::fn_mod::*;
| ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:103:75
--> $DIR/wildcard_imports.rs:131:75
|
LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
| ^ help: try: `inner_extern_foo`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:104:13
--> $DIR/wildcard_imports.rs:132:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:116:20
--> $DIR/wildcard_imports.rs:144:20
|
LL | use self::{inner::*, inner2::*};
| ^^^^^^^^ help: try: `inner::inner_foo`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:116:30
--> $DIR/wildcard_imports.rs:144:30
|
LL | use self::{inner::*, inner2::*};
| ^^^^^^^^^ help: try: `inner2::inner_bar`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:123:13
--> $DIR/wildcard_imports.rs:151:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:152:9
--> $DIR/wildcard_imports.rs:180:9
|
LL | use crate::in_fn_test::*;
| ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:161:9
--> $DIR/wildcard_imports.rs:189:9
|
LL | use crate:: in_fn_test:: * ;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:162:9
--> $DIR/wildcard_imports.rs:190:9
|
LL | use crate:: fn_mod::
| _________^
@ -94,40 +100,40 @@ LL | | *;
| |_________^ help: try: `crate:: fn_mod::foo`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:173:13
--> $DIR/wildcard_imports.rs:201:13
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::foofoo`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:208:17
--> $DIR/wildcard_imports.rs:236:17
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::insidefoo`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:216:13
--> $DIR/wildcard_imports.rs:244:13
|
LL | use crate::super_imports::*;
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:225:17
--> $DIR/wildcard_imports.rs:253:17
|
LL | use super::super::*;
| ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:234:13
--> $DIR/wildcard_imports.rs:262:13
|
LL | use super::super::super_imports::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
error: usage of wildcard import
--> $DIR/wildcard_imports.rs:242:13
--> $DIR/wildcard_imports.rs:270:13
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::foofoo`
error: aborting due to 21 previous errors
error: aborting due to 22 previous errors

View File

@ -64,6 +64,34 @@ mod struct_mod {
}
}
// issue 9942
mod underscore_mod {
// allow use of `deref` so that `clippy --fix` includes `Deref`.
#![allow(noop_method_call)]
mod exports_underscore {
pub use std::ops::Deref as _;
pub fn dummy() {}
}
mod exports_underscore_ish {
pub use std::ops::Deref as _Deref;
pub fn dummy() {}
}
fn does_not_lint() {
use exports_underscore::*;
let _ = (&0).deref();
dummy();
}
fn does_lint() {
use exports_underscore_ish::{_Deref, dummy};
let _ = (&0).deref();
dummy();
}
}
fn main() {
foo();
multi_foo();

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