Merge commit '371120bdbf58a331db5dcfb2d9cddc040f486de8' into clippyup

This commit is contained in:
Philipp Krones 2023-05-05 17:45:49 +02:00
parent 8518391e72
commit 7e9abb311d
152 changed files with 2226 additions and 440 deletions

View File

@ -36,6 +36,12 @@ jobs:
- name: Check *.md files
run: git ls-files -z '*.md' | xargs -0 -n 1 -I {} ./node_modules/.bin/remark {} -u lint -f > /dev/null
- name: Linkcheck book
run: |
rustup toolchain install nightly --component rust-docs
curl https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh -o linkcheck.sh
sh linkcheck.sh clippy --path ./book
- name: Build mdbook
run: mdbook build book

View File

@ -44,7 +44,7 @@ Current stable, released 2023-04-20
### Enhancements
* [`arithmetic_side_effects`]: No longer lints, if safe constant values are used.
* [`arithmetic_side_effects`]: No longer lints if safe constant values are used.
[#10310](https://github.com/rust-lang/rust-clippy/pull/10310)
* [`needless_lifetimes`]: Now works in local macros
[#10257](https://github.com/rust-lang/rust-clippy/pull/10257)
@ -60,39 +60,39 @@ Current stable, released 2023-04-20
### False Positive Fixes
* [`explicit_auto_deref`]: Now considers projections, when determining if auto deref is applicable
* [`explicit_auto_deref`]: Now considers projections when determining if auto deref is applicable
[#10386](https://github.com/rust-lang/rust-clippy/pull/10386)
* [`manual_let_else`]: Now considers side effects of branches, before linting
* [`manual_let_else`]: Now considers side effects of branches before linting
[#10336](https://github.com/rust-lang/rust-clippy/pull/10336)
* [`uninlined_format_args`]: No longer lints for arguments with generic parameters
[#10343](https://github.com/rust-lang/rust-clippy/pull/10343)
* [`needless_lifetimes`]: No longer lints signatures in macros, if the lifetime is a metavariable
* [`needless_lifetimes`]: No longer lints signatures in macros if the lifetime is a metavariable
[#10380](https://github.com/rust-lang/rust-clippy/pull/10380)
* [`len_without_is_empty`]: No longer lints, if `len` as a non-default signature
* [`len_without_is_empty`]: No longer lints if `len` as a non-default signature
[#10255](https://github.com/rust-lang/rust-clippy/pull/10255)
* [`unusual_byte_groupings`]: Relaxed the required restrictions for specific sizes, to reduce false
* [`unusual_byte_groupings`]: Relaxed the required restrictions for specific sizes to reduce false
positives
[#10353](https://github.com/rust-lang/rust-clippy/pull/10353)
* [`manual_let_else`]: No longer lints `if-else` blocks if they can divergent
[#10332](https://github.com/rust-lang/rust-clippy/pull/10332)
* [`expect_used`], [`unwrap_used`], [`dbg_macro`], [`print_stdout`], [`print_stderr`]: No longer lint
in test functions, if `allow-expect-in-tests` is set
in test functions if `allow-expect-in-tests` is set
[#10391](https://github.com/rust-lang/rust-clippy/pull/10391)
* [`unnecessary_safety_comment`]: No longer lints code inside macros
[#10106](https://github.com/rust-lang/rust-clippy/pull/10106)
* [`never_loop`]: No longer lints, for statements following break statements for outer blocks.
* [`never_loop`]: No longer lints statements following break statements for outer blocks.
[#10311](https://github.com/rust-lang/rust-clippy/pull/10311)
### Suggestion Fixes/Improvements
* [`box_default`]: The suggestion now includes the type for trait objects, when needed
* [`box_default`]: The suggestion now includes the type for trait objects when needed
[#10382](https://github.com/rust-lang/rust-clippy/pull/10382)
* [`cast_possible_truncation`]: Now suggests using `try_from` or allowing the lint
[#10038](https://github.com/rust-lang/rust-clippy/pull/10038)
* [`invalid_regex`]: Regex errors for non-literals or regular strings containing escape sequences will
now show the complete error
[#10231](https://github.com/rust-lang/rust-clippy/pull/10231)
* [`transmutes_expressible_as_ptr_casts`]: The suggestion now works, if the base type is borrowed
* [`transmutes_expressible_as_ptr_casts`]: The suggestion now works if the base type is borrowed
[#10193](https://github.com/rust-lang/rust-clippy/pull/10193)
* [`needless_return`]: Now removes all semicolons on the same line
[#10187](https://github.com/rust-lang/rust-clippy/pull/10187)
@ -113,7 +113,7 @@ Current stable, released 2023-04-20
### ICE Fixes
* [`needless_pass_by_value`]: Fixed an ICE, caused by how late bounds were handled
* [`needless_pass_by_value`]: Fixed an ICE caused by how late bounds were handled
[#10328](https://github.com/rust-lang/rust-clippy/pull/10328)
* [`needless_borrow`]: No longer panics on ambiguous projections
[#10403](https://github.com/rust-lang/rust-clippy/pull/10403)
@ -4582,6 +4582,7 @@ Released 2018-09-13
[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
[`default_constructed_unit_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_constructed_unit_structs
[`default_instead_of_iter_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_instead_of_iter_empty
[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
@ -4797,6 +4798,7 @@ Released 2018-09-13
[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
[`manual_while_let_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_while_let_some
[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
@ -4864,6 +4866,7 @@ Released 2018-09-13
[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
[`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign
[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect

View File

@ -1,6 +1,6 @@
[package]
name = "clippy"
version = "0.1.70"
version = "0.1.71"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"

View File

@ -91,7 +91,8 @@ cargo clippy
#### Automatically applying Clippy suggestions
Clippy can automatically apply some lint suggestions, just like the compiler.
Clippy can automatically apply some lint suggestions, just like the compiler. Note that `--fix` implies
`--all-targets`, so it can fix as much code as it can.
```terminal
cargo clippy --fix

View File

@ -50,7 +50,7 @@ questions already, but the parser is okay with it. This is what we
mean when we say `EarlyLintPass` deals with only syntax on the AST level.
Alternatively, think of the `foo_functions` lint we mentioned in
define new lints chapter.
define new lints <!-- FIXME: add link --> chapter.
We want the `foo_functions` lint to detect functions with `foo` as their name.
Writing a lint that only checks for the name of a function means that we only

View File

@ -139,7 +139,7 @@ whether the pattern matched.
## Pattern syntax
The following examples demonstate the pattern syntax:
The following examples demonstrate the pattern syntax:
#### Any (`_`)

View File

@ -13,6 +13,8 @@ Please use that command to update the file and do not edit it by hand.
| [msrv](#msrv) | `None` |
| [cognitive-complexity-threshold](#cognitive-complexity-threshold) | `25` |
| [disallowed-names](#disallowed-names) | `["foo", "baz", "quux"]` |
| [semicolon-inside-block-ignore-singleline](#semicolon-inside-block-ignore-singleline) | `false` |
| [semicolon-outside-block-ignore-multiline](#semicolon-outside-block-ignore-multiline) | `false` |
| [doc-valid-idents](#doc-valid-idents) | `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` |
| [too-many-arguments-threshold](#too-many-arguments-threshold) | `7` |
| [type-complexity-threshold](#type-complexity-threshold) | `250` |
@ -203,6 +205,22 @@ default configuration of Clippy. By default, any configuration will replace the
* [disallowed_names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names)
### semicolon-inside-block-ignore-singleline
Whether to lint only if it's multiline.
**Default Value:** `false` (`bool`)
* [semicolon_inside_block](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block)
### semicolon-outside-block-ignore-multiline
Whether to lint only if it's singleline.
**Default Value:** `false` (`bool`)
* [semicolon_outside_block](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block)
### doc-valid-idents
The list of words this lint should not consider as identifiers needing ticks. The value
`".."` can be used as part of the list to indicate, that the configured values should be appended to the

View File

@ -111,7 +111,8 @@ fn main() {
### Automatically applying Clippy suggestions
Clippy can automatically apply some lint suggestions, just like the compiler.
Clippy can automatically apply some lint suggestions, just like the compiler. Note that `--fix` implies
`--all-targets`, so it can fix as much code as it can.
```terminal
cargo clippy --fix

View File

@ -1,4 +1,4 @@
use crate::clippy_project_root;
use crate::{clippy_project_root, exit_if_err};
use std::process::Command;
/// # Panics
@ -10,7 +10,7 @@ pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool) {
cmd.current_dir(clippy_project_root())
.args(["test", "--test", "dogfood"])
.args(["--features", "internal"])
.args(["--", "dogfood_clippy"]);
.args(["--", "dogfood_clippy", "--nocapture"]);
let mut dogfood_args = Vec::new();
if fix {
@ -27,7 +27,5 @@ pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool) {
cmd.env("__CLIPPY_DOGFOOD_ARGS", dogfood_args.join(" "));
let output = cmd.output().expect("failed to run command");
println!("{}", String::from_utf8_lossy(&output.stdout));
exit_if_err(cmd.status());
}

View File

@ -10,7 +10,9 @@
extern crate rustc_driver;
extern crate rustc_lexer;
use std::io;
use std::path::PathBuf;
use std::process::{self, ExitStatus};
pub mod bless;
pub mod dogfood;
@ -58,3 +60,14 @@ pub fn clippy_project_root() -> PathBuf {
}
panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
}
pub fn exit_if_err(status: io::Result<ExitStatus>) {
match status.expect("failed to run command").code() {
Some(0) => {},
Some(n) => process::exit(n),
None => {
eprintln!("Killed by signal");
process::exit(1);
},
}
}

View File

@ -1,17 +1,6 @@
use crate::cargo_clippy_path;
use std::process::{self, Command, ExitStatus};
use std::{fs, io};
fn exit_if_err(status: io::Result<ExitStatus>) {
match status.expect("failed to run command").code() {
Some(0) => {},
Some(n) => process::exit(n),
None => {
eprintln!("Killed by signal");
process::exit(1);
},
}
}
use crate::{cargo_clippy_path, exit_if_err};
use std::fs;
use std::process::{self, Command};
pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) {
let is_file = match fs::metadata(path) {

View File

@ -36,60 +36,6 @@ pub enum UpdateMode {
pub fn update(update_mode: UpdateMode) {
let (lints, deprecated_lints, renamed_lints) = gather_all();
generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
remove_old_files(update_mode);
}
/// Remove files no longer needed after <https://github.com/rust-lang/rust-clippy/pull/9541>
/// that may be reintroduced unintentionally
///
/// FIXME: This is a temporary measure that should be removed when there are no more PRs that
/// include the stray files
fn remove_old_files(update_mode: UpdateMode) {
let mut failed = false;
let mut remove_file = |path: &Path| match update_mode {
UpdateMode::Check => {
if path.exists() {
failed = true;
println!("unexpected file: {}", path.display());
}
},
UpdateMode::Change => {
if fs::remove_file(path).is_ok() {
println!("removed file: {}", path.display());
}
},
};
let files = [
"clippy_lints/src/lib.register_all.rs",
"clippy_lints/src/lib.register_cargo.rs",
"clippy_lints/src/lib.register_complexity.rs",
"clippy_lints/src/lib.register_correctness.rs",
"clippy_lints/src/lib.register_internal.rs",
"clippy_lints/src/lib.register_lints.rs",
"clippy_lints/src/lib.register_nursery.rs",
"clippy_lints/src/lib.register_pedantic.rs",
"clippy_lints/src/lib.register_perf.rs",
"clippy_lints/src/lib.register_restriction.rs",
"clippy_lints/src/lib.register_style.rs",
"clippy_lints/src/lib.register_suspicious.rs",
"src/docs.rs",
];
for file in files {
remove_file(Path::new(file));
}
if let Ok(docs_dir) = fs::read_dir("src/docs") {
for doc_file in docs_dir {
let path = doc_file.unwrap().path();
remove_file(&path);
}
}
if failed {
exit_with_failure();
}
}
fn generate_lint_files(

View File

@ -1,6 +1,6 @@
[package]
name = "clippy_lints"
version = "0.1.70"
version = "0.1.71"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"

View File

@ -2,7 +2,8 @@ use ast::AttrStyle;
use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_ast as ast;
use rustc_errors::Applicability;
use rustc_lint::{LateContext, LateLintPass};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
@ -51,6 +52,7 @@ impl LateLintPass<'_> for AllowAttribute {
// Separate each crate's features.
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
if_chain! {
if !in_external_macro(cx.sess(), attr.span);
if cx.tcx.features().lint_reasons;
if let AttrStyle::Outer = attr.style;
if let Some(ident) = attr.ident();

View File

@ -638,7 +638,7 @@ declare_clippy_lint! {
#[clippy::version = "1.66.0"]
pub AS_PTR_CAST_MUT,
nursery,
"casting the result of the `&self`-taking `as_ptr` to a mutabe pointer"
"casting the result of the `&self`-taking `as_ptr` to a mutable pointer"
}
declare_clippy_lint! {

View File

@ -141,9 +141,9 @@ fn lint_unnecessary_cast(
fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> {
match expr.kind {
ExprKind::Lit(ref lit) => Some(lit),
ExprKind::Lit(lit) => Some(lit),
ExprKind::Unary(UnOp::Neg, e) => {
if let ExprKind::Lit(ref lit) = e.kind {
if let ExprKind::Lit(lit) = e.kind {
Some(lit)
} else {
None

View File

@ -591,7 +591,7 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &De
conds,
|e| hash_expr(cx, e),
|lhs, rhs| {
// Ignore eq_expr side effects iff one of the expressin kind is a method call
// Ignore eq_expr side effects iff one of the expression kind is a method call
// and the caller is not a mutable, including inner mutable type.
if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind {
if method_caller_is_mutable(cx, caller, ignored_ty_ids) {

View File

@ -105,6 +105,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::dbg_macro::DBG_MACRO_INFO,
crate::default::DEFAULT_TRAIT_ACCESS_INFO,
crate::default::FIELD_REASSIGN_WITH_DEFAULT_INFO,
crate::default_constructed_unit_structs::DEFAULT_CONSTRUCTED_UNIT_STRUCTS_INFO,
crate::default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY_INFO,
crate::default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK_INFO,
crate::default_union_representation::DEFAULT_UNION_REPRESENTATION_INFO,
@ -249,6 +250,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::loops::MANUAL_FIND_INFO,
crate::loops::MANUAL_FLATTEN_INFO,
crate::loops::MANUAL_MEMCPY_INFO,
crate::loops::MANUAL_WHILE_LET_SOME_INFO,
crate::loops::MISSING_SPIN_LOOP_INFO,
crate::loops::MUT_RANGE_BOUND_INFO,
crate::loops::NEEDLESS_RANGE_LOOP_INFO,
@ -445,6 +447,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO,
crate::needless_bool::BOOL_COMPARISON_INFO,
crate::needless_bool::NEEDLESS_BOOL_INFO,
crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
crate::needless_continue::NEEDLESS_CONTINUE_INFO,
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,

View File

@ -0,0 +1,72 @@
use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro, match_def_path, paths};
use hir::{def::Res, ExprKind};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Check for construction on unit struct using `default`.
///
/// ### Why is this bad?
/// This adds code complexity and an unnecessary function call.
///
/// ### Example
/// ```rust
/// # use std::marker::PhantomData;
/// #[derive(Default)]
/// struct S<T> {
/// _marker: PhantomData<T>
/// }
///
/// let _: S<i32> = S {
/// _marker: PhantomData::default()
/// };
/// ```
/// Use instead:
/// ```rust
/// # use std::marker::PhantomData;
/// struct S<T> {
/// _marker: PhantomData<T>
/// }
///
/// let _: S<i32> = S {
/// _marker: PhantomData
/// };
/// ```
#[clippy::version = "1.71.0"]
pub DEFAULT_CONSTRUCTED_UNIT_STRUCTS,
complexity,
"unit structs can be contructed without calling `default`"
}
declare_lint_pass!(DefaultConstructedUnitStructs => [DEFAULT_CONSTRUCTED_UNIT_STRUCTS]);
impl LateLintPass<'_> for DefaultConstructedUnitStructs {
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if_chain!(
// make sure we have a call to `Default::default`
if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind;
if let ExprKind::Path(ref qpath@ hir::QPath::TypeRelative(_,_)) = fn_expr.kind;
if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id);
if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
// make sure we have a struct with no fields (unit struct)
if let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind();
if def.is_struct();
if let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant();
if !var.is_field_list_non_exhaustive() && !is_from_proc_macro(cx, expr);
then {
span_lint_and_sugg(
cx,
DEFAULT_CONSTRUCTED_UNIT_STRUCTS,
expr.span.with_lo(qpath.qself_span().hi()),
"use of `default` to create a unit struct",
"remove this call to `default`",
String::new(),
Applicability::MachineApplicable,
)
}
);
}
}

View File

@ -92,10 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
if trait_item.id.owner_id.def_id == fn_def_id {
// be sure we have `self` parameter in this function
if trait_item.kind == (AssocItemKind::Fn { has_self: true }) {
trait_self_ty = Some(
TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id())
.self_ty(),
);
trait_self_ty =
Some(TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id()).self_ty());
}
}
}

View File

@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
let ty = cx.typeck_results().expr_ty(expr);
if_chain! {
if let ty::Float(fty) = *ty.kind();
if let hir::ExprKind::Lit(ref lit) = expr.kind;
if let hir::ExprKind::Lit(lit) = expr.kind;
if let LitKind::Float(sym, lit_float_ty) = lit.node;
then {
let sym_str = sym.as_str();

View File

@ -2,9 +2,10 @@ use clippy_utils::consts::{
constant, constant_simple, Constant,
Constant::{Int, F32, F64},
};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher;
use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, peel_blocks, sugg};
use clippy_utils::{
diagnostics::span_lint_and_sugg, eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate,
numeric_literal, peel_blocks, sugg,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
@ -677,7 +678,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
{
let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
if_chain! {
if let ExprKind::Lit(ref literal) = mul_lhs.kind;
if let ExprKind::Lit(literal) = mul_lhs.kind;
if let ast::LitKind::Float(ref value, float_type) = literal.node;
if float_type == ast::LitFloatType::Unsuffixed;
then {
@ -703,7 +704,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
{
let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
if_chain! {
if let ExprKind::Lit(ref literal) = mul_lhs.kind;
if let ExprKind::Lit(literal) = mul_lhs.kind;
if let ast::LitKind::Float(ref value, float_type) = literal.node;
if float_type == ast::LitFloatType::Unsuffixed;
then {
@ -730,7 +731,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// All of these operations are currently not const.
// All of these operations are currently not const and are in std.
if in_constant(cx, expr.hir_id) {
return;
}
@ -738,7 +739,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind {
let recv_ty = cx.typeck_results().expr_ty(receiver);
if recv_ty.is_floating_point() {
if recv_ty.is_floating_point() && !is_no_std_crate(cx) {
match path.ident.name.as_str() {
"ln" => check_ln1p(cx, expr, receiver),
"log" => check_log_base(cx, expr, receiver, args),
@ -749,10 +750,12 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
}
}
} else {
check_expm1(cx, expr);
check_mul_add(cx, expr);
check_custom_abs(cx, expr);
check_log_division(cx, expr);
if !is_no_std_crate(cx) {
check_expm1(cx, expr);
check_mul_add(cx, expr);
check_custom_abs(cx, expr);
check_log_division(cx, expr);
}
check_radians(cx, expr);
}
}

View File

@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
);
}
let message = format!("replace the `Into` implentation with `From<{}>`", middle_trait_ref.self_ty());
let message = format!("replace the `Into` implementation with `From<{}>`", middle_trait_ref.self_ty());
if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) {
diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable);
} else {

View File

@ -40,7 +40,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
};
// Body must be &(mut) <self_data>.name
// self_data is not neccessarilly self, to also lint sub-getters, etc…
// self_data is not necessarily self, to also lint sub-getters, etc…
let block_expr = if_chain! {
if let ExprKind::Block(block,_) = body.value.kind;

View File

@ -252,6 +252,11 @@ declare_clippy_lint! {
/// A `Result` is at least as large as the `Err`-variant. While we
/// expect that variant to be seldomly used, the compiler needs to reserve
/// and move that much memory every single time.
/// Furthermore, errors are often simply passed up the call-stack, making
/// use of the `?`-operator and its type-conversion mechanics. If the
/// `Err`-variant further up the call-stack stores the `Err`-variant in
/// question (as library code often does), it itself needs to be at least
/// as large, propagating the problem.
///
/// ### Known problems
/// The size determined by Clippy is platform-dependent.
@ -330,7 +335,7 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
/// Lints when `impl Trait` is being used in a function's paremeters.
/// Lints when `impl Trait` is being used in a function's parameters.
/// ### Why is this bad?
/// Turbofish syntax (`::<>`) cannot be used when `impl Trait` is being used, making `impl Trait` less powerful. Readability may also be a factor.
///

View File

@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
if expr1.span.ctxt() == ctxt;
if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target);
if BinOpKind::Add == op1.node;
if let ExprKind::Lit(ref lit) = value.kind;
if let ExprKind::Lit(lit) = value.kind;
if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
if block.expr.is_none();
then {

View File

@ -87,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
// Get the variable name
let var_name = ares_path.segments[0].ident.name.as_str();
match cond_num_val.kind {
ExprKind::Lit(ref cond_lit) => {
ExprKind::Lit(cond_lit) => {
// Check if the constant is zero
if let LitKind::Int(0, _) = cond_lit.node {
if cx.typeck_results().expr_ty(cond_left).is_signed() {

View File

@ -170,7 +170,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
return;
}
// Index is a constant uint.
if let Some(..) = constant(cx, cx.typeck_results(), index) {
if constant(cx, cx.typeck_results(), index).is_some() {
// Let rustc's `const_err` lint handle constant `usize` indexing on arrays.
return;
}

View File

@ -64,20 +64,21 @@ impl LateLintPass<'_> for ItemsAfterTestModule {
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 matches!(item.kind, ItemKind::Mod(_)) {
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 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;
was_test_mod_visited = true;
test_mod_span = Some(item.span);
}
}
}
}
}
}
}
}

View File

@ -11,7 +11,7 @@ declare_clippy_lint! {
/// It checks for the size of a `Future` created by `async fn` or `async {}`.
///
/// ### Why is this bad?
/// Due to the current [unideal implemention](https://github.com/rust-lang/rust/issues/69826) of `Generator`,
/// Due to the current [unideal implementation](https://github.com/rust-lang/rust/issues/69826) of `Generator`,
/// large size of a `Future` may cause stack overflows.
///
/// ### Example

View File

@ -532,7 +532,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex
}
fn is_empty_string(expr: &Expr<'_>) -> bool {
if let ExprKind::Lit(ref lit) = expr.kind {
if let ExprKind::Lit(lit) = expr.kind {
if let LitKind::Str(lit, _) = lit.node {
let lit = lit.as_str();
return lit.is_empty();

View File

@ -6,6 +6,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{BytePos, Span};
declare_clippy_lint! {
/// ### What it does
@ -205,8 +206,13 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
LET_UNDERSCORE_UNTYPED,
local.span,
"non-binding `let` without a type annotation",
None,
"consider adding a type annotation or removing the `let` keyword",
Some(
Span::new(local.pat.span.hi(),
local.pat.span.hi() + BytePos(1),
local.pat.span.ctxt(),
local.pat.span.parent()
)),
"consider adding a type annotation",
);
}
}

View File

@ -94,6 +94,7 @@ mod crate_in_macro_def;
mod create_dir;
mod dbg_macro;
mod default;
mod default_constructed_unit_structs;
mod default_instead_of_iter_empty;
mod default_numeric_fallback;
mod default_union_representation;
@ -933,7 +934,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv())));
store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock));
let semicolon_inside_block_ignore_singleline = conf.semicolon_inside_block_ignore_singleline;
let semicolon_outside_block_ignore_multiline = conf.semicolon_outside_block_ignore_multiline;
store.register_late_pass(move |_| {
Box::new(semicolon_block::SemicolonBlock::new(
semicolon_inside_block_ignore_singleline,
semicolon_outside_block_ignore_multiline,
))
});
store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck));
store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse));
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
@ -963,6 +971,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation));
store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments));
store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule));
store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View File

@ -15,7 +15,7 @@ use rustc_span::symbol::sym;
use std::fmt::Display;
use std::iter::Iterator;
/// Checks for for loops that sequentially copy items from one slice-like
/// Checks for `for` loops that sequentially copy items from one slice-like
/// object to another.
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,

View File

@ -0,0 +1,110 @@
use clippy_utils::{
diagnostics::{multispan_sugg_with_applicability, span_lint_and_then},
match_def_path, paths,
source::snippet,
SpanlessEq,
};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Pat, Stmt, StmtKind, UnOp};
use rustc_lint::LateContext;
use rustc_span::Span;
use std::borrow::Cow;
use super::MANUAL_WHILE_LET_SOME;
/// The kind of statement that the `pop()` call appeared in.
///
/// Depending on whether the value was assigned to a variable or not changes what pattern
/// we use for the suggestion.
#[derive(Copy, Clone)]
enum PopStmt<'hir> {
/// `x.pop().unwrap()` was and assigned to a variable.
/// The pattern of this local variable will be used and the local statement
/// is deleted in the suggestion.
Local(&'hir Pat<'hir>),
/// `x.pop().unwrap()` appeared in an arbitrary expression and was not assigned to a variable.
/// The suggestion will use some placeholder identifier and the `x.pop().unwrap()` expression
/// is replaced with that identifier.
Anonymous,
}
fn report_lint(cx: &LateContext<'_>, pop_span: Span, pop_stmt_kind: PopStmt<'_>, loop_span: Span, receiver_span: Span) {
span_lint_and_then(
cx,
MANUAL_WHILE_LET_SOME,
pop_span,
"you seem to be trying to pop elements from a `Vec` in a loop",
|diag| {
let (pat, pop_replacement) = match pop_stmt_kind {
PopStmt::Local(pat) => (snippet(cx, pat.span, ".."), String::new()),
PopStmt::Anonymous => (Cow::Borrowed("element"), "element".into()),
};
let loop_replacement = format!("while let Some({}) = {}.pop()", pat, snippet(cx, receiver_span, ".."));
multispan_sugg_with_applicability(
diag,
"consider using a `while..let` loop",
Applicability::MachineApplicable,
[(loop_span, loop_replacement), (pop_span, pop_replacement)],
);
},
);
}
fn match_method_call(cx: &LateContext<'_>, expr: &Expr<'_>, method: &[&str]) -> bool {
if let ExprKind::MethodCall(..) = expr.kind
&& let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
{
match_def_path(cx, id, method)
} else {
false
}
}
fn is_vec_pop_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, is_empty_recv: &Expr<'_>) -> bool {
if (match_method_call(cx, expr, &paths::OPTION_UNWRAP) || match_method_call(cx, expr, &paths::OPTION_EXPECT))
&& let ExprKind::MethodCall(_, unwrap_recv, ..) = expr.kind
&& match_method_call(cx, unwrap_recv, &paths::VEC_POP)
&& let ExprKind::MethodCall(_, pop_recv, ..) = unwrap_recv.kind
{
// make sure they're the same `Vec`
SpanlessEq::new(cx).eq_expr(pop_recv, is_empty_recv)
} else {
false
}
}
fn check_local(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, loop_span: Span) {
if let StmtKind::Local(local) = stmt.kind
&& let Some(init) = local.init
&& is_vec_pop_unwrap(cx, init, is_empty_recv)
{
report_lint(cx, stmt.span, PopStmt::Local(local.pat), loop_span, is_empty_recv.span);
}
}
fn check_call_arguments(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, loop_span: Span) {
if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind {
if let ExprKind::MethodCall(.., args, _) | ExprKind::Call(_, args) = expr.kind {
let offending_arg = args
.iter()
.find_map(|arg| is_vec_pop_unwrap(cx, arg, is_empty_recv).then_some(arg.span));
if let Some(offending_arg) = offending_arg {
report_lint(cx, offending_arg, PopStmt::Anonymous, loop_span, is_empty_recv.span);
}
}
}
}
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, full_cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>, loop_span: Span) {
if let ExprKind::Unary(UnOp::Not, cond) = full_cond.kind
&& let ExprKind::MethodCall(_, is_empty_recv, _, _) = cond.kind
&& match_method_call(cx, cond, &paths::VEC_IS_EMPTY)
&& let ExprKind::Block(body, _) = body.kind
&& let Some(stmt) = body.stmts.first()
{
check_local(cx, stmt, is_empty_recv, loop_span);
check_call_arguments(cx, stmt, is_empty_recv, loop_span);
}
}

View File

@ -7,6 +7,7 @@ mod iter_next_loop;
mod manual_find;
mod manual_flatten;
mod manual_memcpy;
mod manual_while_let_some;
mod missing_spin_loop;
mod mut_range_bound;
mod needless_range_loop;
@ -575,6 +576,36 @@ declare_clippy_lint! {
"manual implementation of `Iterator::find`"
}
declare_clippy_lint! {
/// ### What it does
/// Looks for loops that check for emptiness of a `Vec` in the condition and pop an element
/// in the body as a separate operation.
///
/// ### Why is this bad?
/// Such loops can be written in a more idiomatic way by using a while-let loop and directly
/// pattern matching on the return value of `Vec::pop()`.
///
/// ### Example
/// ```rust
/// let mut numbers = vec![1, 2, 3, 4, 5];
/// while !numbers.is_empty() {
/// let number = numbers.pop().unwrap();
/// // use `number`
/// }
/// ```
/// Use instead:
/// ```rust
/// let mut numbers = vec![1, 2, 3, 4, 5];
/// while let Some(number) = numbers.pop() {
/// // use `number`
/// }
/// ```
#[clippy::version = "1.70.0"]
pub MANUAL_WHILE_LET_SOME,
style,
"checking for emptiness of a `Vec` in the loop condition and popping an element in the body"
}
declare_lint_pass!(Loops => [
MANUAL_MEMCPY,
MANUAL_FLATTEN,
@ -594,6 +625,7 @@ declare_lint_pass!(Loops => [
SINGLE_ELEMENT_LOOP,
MISSING_SPIN_LOOP,
MANUAL_FIND,
MANUAL_WHILE_LET_SOME
]);
impl<'tcx> LateLintPass<'tcx> for Loops {
@ -640,9 +672,10 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
while_let_on_iterator::check(cx, expr);
if let Some(higher::While { condition, body }) = higher::While::hir(expr) {
if let Some(higher::While { condition, body, span }) = higher::While::hir(expr) {
while_immutable_condition::check(cx, condition, body);
missing_spin_loop::check(cx, condition, body);
manual_while_let_some::check(cx, condition, body, span);
}
}
}

View File

@ -208,7 +208,7 @@ fn is_end_eq_array_len<'tcx>(
indexed_ty: Ty<'tcx>,
) -> bool {
if_chain! {
if let ExprKind::Lit(ref lit) = end.kind;
if let ExprKind::Lit(lit) = end.kind;
if let ast::LitKind::Int(end_int, _) = lit.node;
if let ty::Array(_, arr_len_const) = indexed_ty.kind();
if let Some(arr_len) = arr_len_const.try_eval_target_usize(cx.tcx, cx.param_env);

View File

@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert {
};
let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par();
let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});");
// we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block
// we show to the user the suggestion without the comments, but when applying the fix, include the comments in the block
span_lint_and_then(
cx,
MANUAL_ASSERT,

View File

@ -101,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
if source != MatchSource::Normal {
return;
}
// Any other number than two arms doesn't (neccessarily)
// Any other number than two arms doesn't (necessarily)
// have a trivial mapping to let else.
if arms.len() != 2 {
return;

View File

@ -46,7 +46,7 @@ declare_clippy_lint! {
#[clippy::version = "1.64.0"]
pub MANUAL_RETAIN,
perf,
"`retain()` is simpler and the same functionalitys"
"`retain()` is simpler and the same functionalities"
}
pub struct ManualRetain {

View File

@ -22,7 +22,7 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]
if arms.len() == 2 {
// no guards
let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind {
if let ExprKind::Lit(ref lit) = arm_bool.kind {
if let ExprKind::Lit(lit) = arm_bool.kind {
match lit.node {
LitKind::Bool(true) => Some((arms[0].body, arms[1].body)),
LitKind::Bool(false) => Some((arms[1].body, arms[0].body)),

View File

@ -63,8 +63,11 @@ fn find_sugg_for_if_let<'tcx>(
// Determine which function should be used, and the type contained by the corresponding
// variant.
let (good_method, inner_ty) = match check_pat.kind {
PatKind::TupleStruct(ref qpath, [sub_pat], _) => {
if let PatKind::Wild = sub_pat.kind {
PatKind::TupleStruct(ref qpath, args, rest) => {
let is_wildcard = matches!(args.first().map(|p| &p.kind), Some(PatKind::Wild));
let is_rest = matches!((args, rest.as_opt_usize()), ([], Some(_)));
if is_wildcard || is_rest {
let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id);
let Some(id) = res.opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) else { return };
let lang_items = cx.tcx.lang_items();
@ -334,7 +337,7 @@ fn find_good_method_for_match<'a>(
};
match body_node_pair {
(ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
(ExprKind::Lit(lit_left), ExprKind::Lit(lit_right)) => match (&lit_left.node, &lit_right.node) {
(LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
(LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
_ => None,

View File

@ -18,7 +18,7 @@ pub(super) fn check(
) -> bool {
if_chain! {
if let Some(args) = method_chain_args(info.chain, chain_methods);
if let hir::ExprKind::Lit(ref lit) = info.other.kind;
if let hir::ExprKind::Lit(lit) = info.other.kind;
if let ast::LitKind::Char(c) = lit.node;
then {
let mut applicability = Applicability::MachineApplicable;

View File

@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal
if let hir::ExprKind::Index(caller_var, index_expr) = &caller_expr.kind;
if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen })
= higher::Range::hir(index_expr);
if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind;
if let hir::ExprKind::Lit(start_lit) = &start_expr.kind;
if let ast::LitKind::Int(start_idx, _) = start_lit.node;
then {
let mut applicability = Applicability::MachineApplicable;

View File

@ -42,11 +42,11 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
// Only proceed if this is a call on some object of type std::fs::OpenOptions
if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && !arguments.is_empty() {
let argument_option = match arguments[0].kind {
ExprKind::Lit(ref span) => {
ExprKind::Lit(span) => {
if let Spanned {
node: LitKind::Bool(lit),
..
} = *span
} = span
{
if *lit { Argument::True } else { Argument::False }
} else {

View File

@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).subst_identity(), sym::PathBuf);
if let ExprKind::Lit(ref lit) = arg.kind;
if let ExprKind::Lit(lit) = arg.kind;
if let LitKind::Str(ref path_lit, _) = lit.node;
if let pushed_path = Path::new(path_lit.as_str());
if let Some(pushed_path_lit) = pushed_path.to_str();

View File

@ -38,7 +38,7 @@ fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)
match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT) {
// check if argument of `SeekFrom::Current` is `0`
if args.len() == 1 &&
let ExprKind::Lit(ref lit) = args[0].kind &&
let ExprKind::Lit(lit) = args[0].kind &&
let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node {
return true
}

View File

@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(
let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() &&
match_def_path(cx, def_id, &paths::STD_IO_SEEKFROM_START) &&
args1.len() == 1 &&
let ExprKind::Lit(ref lit) = args1[0].kind &&
let ExprKind::Lit(lit) = args1[0].kind &&
let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
{
let method_call_span = expr.span.with_lo(name_span.lo());

View File

@ -78,7 +78,7 @@ pub(super) fn check(
}
// Check if the first argument to .fold is a suitable literal
if let hir::ExprKind::Lit(ref lit) = init.kind {
if let hir::ExprKind::Lit(lit) = init.kind {
match lit.node {
ast::LitKind::Bool(false) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, "any", true),
ast::LitKind::Bool(true) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, "all", true),

View File

@ -12,7 +12,7 @@ declare_clippy_lint! {
/// Checks if a provided method is used implicitly by a trait
/// implementation. A usage example would be a wrapper where every method
/// should perform some operation before delegating to the inner type's
/// implemenation.
/// implementation.
///
/// This lint should typically be enabled on a specific trait `impl` item
/// rather than globally.

View File

@ -3,10 +3,12 @@
//! This lint is **warn** by default
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::higher;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::{get_parent_node, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt};
use clippy_utils::{
get_parent_node, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt, span_extract_comment,
};
use clippy_utils::{higher, SpanlessEq};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Node, UnOp};
@ -77,7 +79,39 @@ declare_clippy_lint! {
"comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
}
declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]);
declare_clippy_lint! {
/// ### What it does
/// Checks for expressions of the form `if c { x = true } else { x = false }`
/// (or vice versa) and suggest assigning the variable directly from the
/// condition.
///
/// ### Why is this bad?
/// Redundant code.
///
/// ### Example
/// ```rust,ignore
/// # fn must_keep(x: i32, y: i32) -> bool { x == y }
/// # let x = 32; let y = 10;
/// # let mut skip: bool;
/// if must_keep(x, y) {
/// skip = false;
/// } else {
/// skip = true;
/// }
/// ```
/// Use instead:
/// ```rust,ignore
/// # fn must_keep(x: i32, y: i32) -> bool { x == y }
/// # let x = 32; let y = 10;
/// # let mut skip: bool;
/// skip = !must_keep(x, y);
/// ```
#[clippy::version = "1.69.0"]
pub NEEDLESS_BOOL_ASSIGN,
complexity,
"setting the same boolean variable in both branches of an if-statement"
}
declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL, NEEDLESS_BOOL_ASSIGN]);
fn condition_needs_parentheses(e: &Expr<'_>) -> bool {
let mut inner = e;
@ -173,6 +207,29 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
_ => (),
}
}
if let Some((lhs_a, a)) = fetch_assign(then) &&
let Some((lhs_b, b)) = fetch_assign(r#else) &&
SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b) &&
span_extract_comment(cx.tcx.sess.source_map(), e.span).is_empty()
{
let mut applicability = Applicability::MachineApplicable;
let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability);
let lhs = snippet_with_applicability(cx, lhs_a.span, "..", &mut applicability);
let sugg = if a == b {
format!("{cond}; {lhs} = {a:?};")
} else {
format!("{lhs} = {};", if a { cond } else { !cond })
};
span_lint_and_sugg(
cx,
NEEDLESS_BOOL_ASSIGN,
e.span,
"this if-then-else expression assigns a bool literal",
"you can reduce it to",
sugg,
applicability
);
}
}
}
}
@ -369,10 +426,18 @@ fn fetch_bool_block(expr: &Expr<'_>) -> Option<Expression> {
}
fn fetch_bool_expr(expr: &Expr<'_>) -> Option<bool> {
if let ExprKind::Lit(ref lit_ptr) = peel_blocks(expr).kind {
if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind {
if let LitKind::Bool(value) = lit_ptr.node {
return Some(value);
}
}
None
}
fn fetch_assign<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, bool)> {
if let ExprKind::Assign(lhs, rhs, _) = peel_blocks_with_stmt(expr).kind {
fetch_bool_expr(rhs).map(|b| (lhs, b))
} else {
None
}
}

View File

@ -49,14 +49,14 @@ fn snippet_enclosed_in_parenthesis(snippet: &str) -> bool {
fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) {
if is_start &&
let ExprKind::Lit(ref literal) = e.kind &&
let ExprKind::Lit(literal) = e.kind &&
let ast::LitKind::Float(_sym, ast::LitFloatType::Unsuffixed) = literal.node
{
// don't check floating point literals on the start expression of a range
return;
}
if_chain! {
if let ExprKind::Lit(ref literal) = e.kind;
if let ExprKind::Lit(literal) = e.kind;
// the indicator that parenthesis surround the literal is that the span of the expression and the literal differ
if (literal.span.data().hi - literal.span.data().lo) != (e.span.data().hi - e.span.data().lo);
// inspect the source code of the expression for parenthesis

View File

@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for NegMultiply {
fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
if_chain! {
if let ExprKind::Lit(ref l) = lit.kind;
if let ExprKind::Lit(l) = lit.kind;
if consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1);
if cx.typeck_results().expr_ty(exp).is_integral();

View File

@ -110,7 +110,7 @@ impl ArithmeticSideEffects {
/// like `i32::MAX` or constant references like `N` from `const N: i32 = 1;`,
fn literal_integer(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<u128> {
let actual = peel_hir_expr_unary(expr).0;
if let hir::ExprKind::Lit(ref lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node {
if let hir::ExprKind::Lit(lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node {
return Some(n)
}
if let Some((Constant::Int(n), _)) = constant(cx, cx.typeck_results(), expr) {

View File

@ -180,7 +180,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
.allow_invalid_utf8(!utf8)
.build();
if let ExprKind::Lit(ref lit) = expr.kind {
if let ExprKind::Lit(lit) = expr.kind {
if let LitKind::Str(ref r, style) = lit.node {
let r = r.as_str();
let offset = if let StrStyle::Raw(n) = style { 2 + n } else { 1 };

View File

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
declare_clippy_lint! {
@ -64,7 +64,78 @@ declare_clippy_lint! {
restriction,
"add a semicolon outside the block"
}
declare_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]);
impl_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]);
#[derive(Copy, Clone)]
pub struct SemicolonBlock {
semicolon_inside_block_ignore_singleline: bool,
semicolon_outside_block_ignore_multiline: bool,
}
impl SemicolonBlock {
pub fn new(semicolon_inside_block_ignore_singleline: bool, semicolon_outside_block_ignore_multiline: bool) -> Self {
Self {
semicolon_inside_block_ignore_singleline,
semicolon_outside_block_ignore_multiline,
}
}
fn semicolon_inside_block(self, cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) {
let insert_span = tail.span.source_callsite().shrink_to_hi();
let remove_span = semi_span.with_lo(block.span.hi());
if self.semicolon_inside_block_ignore_singleline && get_line(cx, remove_span) == get_line(cx, insert_span) {
return;
}
span_lint_and_then(
cx,
SEMICOLON_INSIDE_BLOCK,
semi_span,
"consider moving the `;` inside the block for consistent formatting",
|diag| {
multispan_sugg_with_applicability(
diag,
"put the `;` here",
Applicability::MachineApplicable,
[(remove_span, String::new()), (insert_span, ";".to_owned())],
);
},
);
}
fn semicolon_outside_block(
self,
cx: &LateContext<'_>,
block: &Block<'_>,
tail_stmt_expr: &Expr<'_>,
semi_span: Span,
) {
let insert_span = block.span.with_lo(block.span.hi());
// account for macro calls
let semi_span = cx.sess().source_map().stmt_span(semi_span, block.span);
let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi());
if self.semicolon_outside_block_ignore_multiline && get_line(cx, remove_span) != get_line(cx, insert_span) {
return;
}
span_lint_and_then(
cx,
SEMICOLON_OUTSIDE_BLOCK,
block.span,
"consider moving the `;` outside the block for consistent formatting",
|diag| {
multispan_sugg_with_applicability(
diag,
"put the `;` here",
Applicability::MachineApplicable,
[(remove_span, String::new()), (insert_span, ";".to_owned())],
);
},
);
}
}
impl LateLintPass<'_> for SemicolonBlock {
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
@ -83,55 +154,23 @@ impl LateLintPass<'_> for SemicolonBlock {
span,
..
} = stmt else { return };
semicolon_outside_block(cx, block, expr, span);
self.semicolon_outside_block(cx, block, expr, span);
},
StmtKind::Semi(Expr {
kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _),
..
}) if !block.span.from_expansion() => semicolon_inside_block(cx, block, tail, stmt.span),
}) if !block.span.from_expansion() => {
self.semicolon_inside_block(cx, block, tail, stmt.span);
},
_ => (),
}
}
}
fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) {
let insert_span = tail.span.source_callsite().shrink_to_hi();
let remove_span = semi_span.with_lo(block.span.hi());
fn get_line(cx: &LateContext<'_>, span: Span) -> Option<usize> {
if let Ok(line) = cx.sess().source_map().lookup_line(span.lo()) {
return Some(line.line);
}
span_lint_and_then(
cx,
SEMICOLON_INSIDE_BLOCK,
semi_span,
"consider moving the `;` inside the block for consistent formatting",
|diag| {
multispan_sugg_with_applicability(
diag,
"put the `;` here",
Applicability::MachineApplicable,
[(remove_span, String::new()), (insert_span, ";".to_owned())],
);
},
);
}
fn semicolon_outside_block(cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>, semi_span: Span) {
let insert_span = block.span.with_lo(block.span.hi());
// account for macro calls
let semi_span = cx.sess().source_map().stmt_span(semi_span, block.span);
let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi());
span_lint_and_then(
cx,
SEMICOLON_OUTSIDE_BLOCK,
block.span,
"consider moving the `;` outside the block for consistent formatting",
|diag| {
multispan_sugg_with_applicability(
diag,
"put the `;` here",
Applicability::MachineApplicable,
[(remove_span, String::new()), (insert_span, ";".to_owned())],
);
},
);
None
}

View File

@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for Shadow {
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
let PatKind::Binding(_, id, ident, _) = pat.kind else { return };
if pat.span.desugaring_kind().is_some() {
if pat.span.desugaring_kind().is_some() || pat.span.from_expansion() {
return;
}

View File

@ -74,7 +74,7 @@ enum InitializationType<'tcx> {
impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// Matches initialization on reassignements. For example: `vec = Vec::with_capacity(100)`
// Matches initialization on reassignments. For example: `vec = Vec::with_capacity(100)`
if_chain! {
if let ExprKind::Assign(left, right, _) = expr.kind;

View File

@ -292,6 +292,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
}
if_chain! {
if !in_external_macro(cx.sess(), e.span);
if let ExprKind::MethodCall(path, receiver, ..) = &e.kind;
if path.ident.name == sym!(as_bytes);
if let ExprKind::Lit(lit) = &receiver.kind;

View File

@ -60,7 +60,7 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_
if let Some(last_field) = data.fields().last();
if let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind;
// Then check if that that array zero-sized
// Then check if that array is zero-sized
let length = Const::from_anon_const(cx.tcx, length.def_id);
let length = length.try_eval_target_usize(cx.tcx, cx.param_env);
if let Some(length) = length;

View File

@ -90,8 +90,8 @@ declare_clippy_lint! {
///
/// ### Why is this bad?
/// `Option<_>` represents an optional value. `Option<Option<_>>`
/// represents an optional optional value which is logically the same thing as an optional
/// value but has an unneeded extra level of wrapping.
/// represents an optional value which itself wraps an optional. This is logically the
/// same thing as an optional value but has an unneeded extra level of wrapping.
///
/// If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,
/// consider a custom `enum` instead, with clear names for each case.

View File

@ -76,7 +76,7 @@ declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_
impl LateLintPass<'_> for Unicode {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
if let ExprKind::Lit(ref lit) = expr.kind {
if let ExprKind::Lit(lit) = expr.kind {
if let LitKind::Str(_, _) | LitKind::Char(_) = lit.node {
check_str(cx, lit.span, expr.hir_id);
}

View File

@ -109,7 +109,7 @@ impl LateLintPass<'_> for UnnecessaryBoxReturns {
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) {
// Ignore implementations of traits, because the lint should be on the
// trait, not on the implmentation of it.
// trait, not on the implementation of it.
let Node::Item(parent) = cx.tcx.hir().get_parent(item.hir_id()) else { return };
let ItemKind::Impl(parent) = parent.kind else { return };
if parent.of_trait.is_some() {

View File

@ -333,7 +333,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
#[allow(clippy::too_many_lines)]
fn expr(&self, expr: &Binding<&hir::Expr<'_>>) {
if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) {
if let Some(higher::While { condition, body, .. }) = higher::While::hir(expr.value) {
bind!(self, condition, body);
chain!(
self,
@ -561,7 +561,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
ExprKind::OffsetOf(container, ref fields) => {
bind!(self, container, fields);
kind!("OffsetOf({container}, {fields})");
}
},
ExprKind::Struct(qpath, fields, base) => {
bind!(self, qpath, fields);
opt_bind!(self, base);

View File

@ -277,6 +277,14 @@ define_Conf! {
/// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
/// default configuration of Clippy. By default, any configuration will replace the default value.
(disallowed_names: Vec<String> = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
/// Lint: SEMICOLON_INSIDE_BLOCK.
///
/// Whether to lint only if it's multiline.
(semicolon_inside_block_ignore_singleline: bool = false),
/// Lint: SEMICOLON_OUTSIDE_BLOCK.
///
/// Whether to lint only if it's singleline.
(semicolon_outside_block_ignore_multiline: bool = false),
/// Lint: DOC_MARKDOWN.
///
/// The list of words this lint should not consider as identifiers needing ticks. The value

View File

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

View File

@ -117,7 +117,7 @@ fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat(tcx, e).1),
ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), expr_search_pat(tcx, e).1),
ExprKind::Unary(UnOp::Neg, e) => (Pat::Str("-"), expr_search_pat(tcx, e).1),
ExprKind::Lit(ref lit) => lit_search_pat(&lit.node),
ExprKind::Lit(lit) => lit_search_pat(&lit.node),
ExprKind::Array(_) | ExprKind::Repeat(..) => (Pat::Str("["), Pat::Str("]")),
ExprKind::Call(e, []) | ExprKind::MethodCall(_, e, [], _) => (expr_search_pat(tcx, e).0, Pat::Str("(")),
ExprKind::Call(first, [.., last])

View File

@ -324,7 +324,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
match e.kind {
ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
ExprKind::Block(block, _) => self.block(block),
ExprKind::Lit(ref lit) => {
ExprKind::Lit(lit) => {
if is_direct_expn_of(e.span, "cfg").is_some() {
None
} else {

View File

@ -311,6 +311,8 @@ pub struct While<'hir> {
pub condition: &'hir Expr<'hir>,
/// `while` loop body
pub body: &'hir Expr<'hir>,
/// Span of the loop header
pub span: Span,
}
impl<'hir> While<'hir> {
@ -336,10 +338,10 @@ impl<'hir> While<'hir> {
},
_,
LoopSource::While,
_,
span,
) = expr.kind
{
return Some(Self { condition, body });
return Some(Self { condition, body, span });
}
None
}

View File

@ -301,7 +301,7 @@ impl HirEqInterExpr<'_, '_, '_> {
(&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re),
(&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
(&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re),
(&ExprKind::OffsetOf(l_container, ref l_fields), &ExprKind::OffsetOf(r_container, ref r_fields)) => {
(&ExprKind::OffsetOf(l_container, l_fields), &ExprKind::OffsetOf(r_container, r_fields)) => {
self.eq_ty(l_container, r_container) && over(l_fields, r_fields, |l, r| l.name == r.name)
},
_ => false,
@ -718,7 +718,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_pat(pat);
},
ExprKind::Err(_) => {},
ExprKind::Lit(ref l) => {
ExprKind::Lit(l) => {
l.node.hash(&mut self.s);
},
ExprKind::Loop(b, ref i, ..) => {

View File

@ -86,10 +86,10 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet};
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
use rustc_hir::{
self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination,
Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, ItemKind, LangItem, Local,
self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr,
ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, ItemKind, LangItem, Local,
MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp,
TraitItem, TraitItemRef, TraitRef, TyKind, UnOp,
};
use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::{LateContext, Level, Lint, LintContext};
@ -197,31 +197,7 @@ pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<
/// }
/// ```
pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
let parent_id = cx.tcx.hir().get_parent_item(id).def_id;
match cx.tcx.hir().get_by_def_id(parent_id) {
Node::Item(&Item {
kind: ItemKind::Const(..) | ItemKind::Static(..) | ItemKind::Enum(..),
..
})
| Node::TraitItem(&TraitItem {
kind: TraitItemKind::Const(..),
..
})
| Node::ImplItem(&ImplItem {
kind: ImplItemKind::Const(..),
..
})
| Node::AnonConst(_) => true,
Node::Item(&Item {
kind: ItemKind::Fn(ref sig, ..),
..
})
| Node::ImplItem(&ImplItem {
kind: ImplItemKind::Fn(ref sig, _),
..
}) => sig.header.constness == Constness::Const,
_ => false,
}
cx.tcx.hir().is_inside_const_context(id)
}
/// Checks if a `Res` refers to a constructor of a `LangItem`
@ -846,7 +822,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
},
ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! {
if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind;
if let ExprKind::Lit(const_lit) = cx.tcx.hir().body(len.body).value.kind;
if let LitKind::Int(v, _) = const_lit.node;
if v <= 32 && is_default_equivalent(cx, x);
then {
@ -875,7 +851,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &
}) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
ExprKind::Repeat(_, ArrayLen::Body(len)) => {
if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind &&
if let ExprKind::Lit(const_lit) = cx.tcx.hir().body(len.body).value.kind &&
let LitKind::Int(v, _) = const_lit.node
{
return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
@ -1569,7 +1545,7 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool
/// Checks whether the given expression is a constant literal of the given value.
pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
// FIXME: use constant folding
if let ExprKind::Lit(ref spanned) = expr.kind {
if let ExprKind::Lit(spanned) = expr.kind {
if let LitKind::Int(v, _) = spanned.node {
return v == value;
}
@ -2165,10 +2141,7 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
.predicates
.iter()
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
traits::impossible_predicates(
cx.tcx,
traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>(),
)
traits::impossible_predicates(cx.tcx, traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>())
}
/// Returns the `DefId` of the callee if the given expression is a function or method call.
@ -2233,8 +2206,12 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<S
None
}
/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
/// `hash` must be comformed with `eq`
/// Returns list of all pairs `(a, b)` where `eq(a, b) == true`
/// and `a` is before `b` in `exprs` for all `a` and `b` in
/// `exprs`
///
/// Given functions `eq` and `hash` such that `eq(a, b) == true`
/// implies `hash(a) == hash(b)`
pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
where
Hash: Fn(&T) -> u64,

View File

@ -362,7 +362,7 @@ thread_local! {
/// able to access the many features of a [`LateContext`].
///
/// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
/// assumption that the early pass the populates the map and the later late passes will all be
/// assumption that the early pass that populates the map and the later late passes will all be
/// running on the same thread.
static AST_FORMAT_ARGS: RefCell<FxHashMap<Span, FormatArgs>> = {
static CALLED: AtomicBool = AtomicBool::new(false);

View File

@ -159,3 +159,7 @@ pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];
pub const INSTANT_NOW: [&str; 4] = ["std", "time", "Instant", "now"];
pub const INSTANT: [&str; 3] = ["std", "time", "Instant"];
pub const VEC_IS_EMPTY: [&str; 4] = ["alloc", "vec", "Vec", "is_empty"];
pub const VEC_POP: [&str; 4] = ["alloc", "vec", "Vec", "pop"];
pub const OPTION_UNWRAP: [&str; 4] = ["core", "option", "Option", "unwrap"];
pub const OPTION_EXPECT: [&str; 4] = ["core", "option", "Option", "expect"];

View File

@ -194,7 +194,9 @@ fn check_rvalue<'tcx>(
))
}
},
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_), _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_), _) | Rvalue::ShallowInitBox(_, _) => {
Ok(())
},
Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, tcx);
if ty.is_integral() || ty.is_bool() {

View File

@ -162,7 +162,7 @@ impl<'a> Sugg<'a> {
get_snippet(lhs.span),
get_snippet(rhs.span),
),
hir::ExprKind::Cast(lhs, ty) => Sugg::BinOp(AssocOp::As, get_snippet(lhs.span), get_snippet(ty.span)),
hir::ExprKind::Cast(lhs, ty) |
//FIXME(chenyukang), remove this after type ascription is removed from AST
hir::ExprKind::Type(lhs, ty) => Sugg::BinOp(AssocOp::As, get_snippet(lhs.span), get_snippet(ty.span)),
}
@ -254,11 +254,7 @@ impl<'a> Sugg<'a> {
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
snippet_with_context(cx, rhs.span, ctxt, default, app).0,
),
ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
AssocOp::As,
snippet_with_context(cx, lhs.span, ctxt, default, app).0,
snippet_with_context(cx, ty.span, ctxt, default, app).0,
),
ast::ExprKind::Cast(ref lhs, ref ty) |
//FIXME(chenyukang), remove this after type ascription is removed from AST
ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
AssocOp::As,
@ -603,8 +599,8 @@ enum Associativity {
#[must_use]
fn associativity(op: AssocOp) -> Associativity {
use rustc_ast::util::parser::AssocOp::{
Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Divide, DotDot, DotDotEq, Equal, Greater,
GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Divide, DotDot, DotDotEq, Equal, Greater, GreaterEqual, LAnd,
LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
};
match op {

View File

@ -93,7 +93,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'
for (predicate, _span) in cx.tcx.explicit_item_bounds(def_id).subst_identity_iter_copied() {
match predicate.kind().skip_binder() {
// For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
// and check substituions to find `U`.
// and check substitutions to find `U`.
ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
if trait_predicate
.trait_ref
@ -837,7 +837,7 @@ pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
if let ty::Adt(adt, _) = ty.kind()
&& let &[krate, .., name] = &*cx.get_def_path(adt.did())
&& let sym::libc | sym::core | sym::std = krate
&& name.as_str() == "c_void"
&& name == rustc_span::sym::c_void
{
true
} else {
@ -1101,7 +1101,7 @@ pub fn make_projection<'tcx>(
///
/// This function is for associated types which are "known" to be valid with the given
/// substitutions, and as such, will only return `None` when debug assertions are disabled in order
/// to prevent ICE's. With debug assertions enabled this will check that that type normalization
/// to prevent ICE's. With debug assertions enabled this will check that type normalization
/// succeeds as well as everything checked by `make_projection`.
pub fn make_normalized_projection<'tcx>(
tcx: TyCtxt<'tcx>,

View File

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

View File

@ -79,9 +79,11 @@ is explicitly specified in the options.
### Fix mode
You can run `cargo lintcheck --fix` which will run Clippy with `--fix` and
print a warning if Clippy's suggestions fail to apply (if the resulting code does not build).
print a warning if Clippy's suggestions fail to apply (if the resulting code does not build).
This lets us spot bad suggestions or false positives automatically in some cases.
> Note: Fix mode implies `--all-targets`, so it can fix as much code as it can.
Please note that the target dir should be cleaned afterwards since clippy will modify
the downloaded sources which can lead to unexpected results when running lintcheck again afterwards.

View File

@ -421,7 +421,7 @@ impl Crate {
{
let subcrate = &stderr[63..];
println!(
"ERROR: failed to apply some suggetion to {} / to (sub)crate {subcrate}",
"ERROR: failed to apply some suggestion to {} / to (sub)crate {subcrate}",
self.name
);
}

View File

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

View File

@ -13,7 +13,7 @@ Usage:
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`
--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

View File

@ -39,7 +39,7 @@ fn dogfood_clippy() {
assert!(
failed_packages.is_empty(),
"Dogfood failed for packages `{}`",
failed_packages.iter().format(", "),
failed_packages.iter().join(", "),
);
}

View File

@ -0,0 +1,86 @@
//@run-rustfix
#![allow(
unused,
clippy::unused_unit,
clippy::unnecessary_operation,
clippy::no_effect,
clippy::single_element_loop
)]
#![warn(clippy::semicolon_inside_block)]
#![warn(clippy::semicolon_outside_block)]
macro_rules! m {
(()) => {
()
};
(0) => {{
0
};};
(1) => {{
1;
}};
(2) => {{
2;
}};
}
fn unit_fn_block() {
()
}
#[rustfmt::skip]
fn main() {
{ unit_fn_block() }
unsafe { unit_fn_block() }
{
unit_fn_block()
}
{ unit_fn_block() };
unsafe { unit_fn_block() };
{ unit_fn_block() };
unsafe { unit_fn_block() };
{ unit_fn_block(); };
unsafe { unit_fn_block(); };
{
unit_fn_block();
unit_fn_block();
}
{
unit_fn_block();
unit_fn_block();
}
{
unit_fn_block();
unit_fn_block();
};
{ m!(()) };
{ m!(()) };
{ m!(()); };
m!(0);
m!(1);
m!(2);
for _ in [()] {
unit_fn_block();
}
for _ in [()] {
unit_fn_block()
}
let _d = || {
unit_fn_block();
};
let _d = || {
unit_fn_block()
};
{ unit_fn_block(); };
unit_fn_block()
}

View File

@ -0,0 +1,86 @@
//@run-rustfix
#![allow(
unused,
clippy::unused_unit,
clippy::unnecessary_operation,
clippy::no_effect,
clippy::single_element_loop
)]
#![warn(clippy::semicolon_inside_block)]
#![warn(clippy::semicolon_outside_block)]
macro_rules! m {
(()) => {
()
};
(0) => {{
0
};};
(1) => {{
1;
}};
(2) => {{
2;
}};
}
fn unit_fn_block() {
()
}
#[rustfmt::skip]
fn main() {
{ unit_fn_block() }
unsafe { unit_fn_block() }
{
unit_fn_block()
}
{ unit_fn_block() };
unsafe { unit_fn_block() };
{ unit_fn_block(); }
unsafe { unit_fn_block(); }
{ unit_fn_block(); };
unsafe { unit_fn_block(); };
{
unit_fn_block();
unit_fn_block()
};
{
unit_fn_block();
unit_fn_block();
}
{
unit_fn_block();
unit_fn_block();
};
{ m!(()) };
{ m!(()); }
{ m!(()); };
m!(0);
m!(1);
m!(2);
for _ in [()] {
unit_fn_block();
}
for _ in [()] {
unit_fn_block()
}
let _d = || {
unit_fn_block();
};
let _d = || {
unit_fn_block()
};
{ unit_fn_block(); };
unit_fn_block()
}

View File

@ -0,0 +1,55 @@
error: consider moving the `;` outside the block for consistent formatting
--> $DIR/both.rs:43:5
|
LL | { unit_fn_block(); }
| ^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::semicolon-outside-block` implied by `-D warnings`
help: put the `;` here
|
LL - { unit_fn_block(); }
LL + { unit_fn_block() };
|
error: consider moving the `;` outside the block for consistent formatting
--> $DIR/both.rs:44:5
|
LL | unsafe { unit_fn_block(); }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: put the `;` here
|
LL - unsafe { unit_fn_block(); }
LL + unsafe { unit_fn_block() };
|
error: consider moving the `;` inside the block for consistent formatting
--> $DIR/both.rs:49:5
|
LL | / {
LL | | unit_fn_block();
LL | | unit_fn_block()
LL | | };
| |______^
|
= note: `-D clippy::semicolon-inside-block` implied by `-D warnings`
help: put the `;` here
|
LL ~ unit_fn_block();
LL ~ }
|
error: consider moving the `;` outside the block for consistent formatting
--> $DIR/both.rs:63:5
|
LL | { m!(()); }
| ^^^^^^^^^^^
|
help: put the `;` here
|
LL - { m!(()); }
LL + { m!(()) };
|
error: aborting due to 4 previous errors

View File

@ -0,0 +1,2 @@
semicolon-inside-block-ignore-singleline = true
semicolon-outside-block-ignore-multiline = true

View File

@ -0,0 +1,85 @@
//@run-rustfix
#![allow(
unused,
clippy::unused_unit,
clippy::unnecessary_operation,
clippy::no_effect,
clippy::single_element_loop
)]
#![warn(clippy::semicolon_inside_block)]
macro_rules! m {
(()) => {
()
};
(0) => {{
0
};};
(1) => {{
1;
}};
(2) => {{
2;
}};
}
fn unit_fn_block() {
()
}
#[rustfmt::skip]
fn main() {
{ unit_fn_block() }
unsafe { unit_fn_block() }
{
unit_fn_block()
}
{ unit_fn_block() };
unsafe { unit_fn_block() };
{ unit_fn_block(); }
unsafe { unit_fn_block(); }
{ unit_fn_block(); };
unsafe { unit_fn_block(); };
{
unit_fn_block();
unit_fn_block();
}
{
unit_fn_block();
unit_fn_block();
}
{
unit_fn_block();
unit_fn_block();
};
{ m!(()) };
{ m!(()); }
{ m!(()); };
m!(0);
m!(1);
m!(2);
for _ in [()] {
unit_fn_block();
}
for _ in [()] {
unit_fn_block()
}
let _d = || {
unit_fn_block();
};
let _d = || {
unit_fn_block()
};
{ unit_fn_block(); };
unit_fn_block()
}

View File

@ -0,0 +1,85 @@
//@run-rustfix
#![allow(
unused,
clippy::unused_unit,
clippy::unnecessary_operation,
clippy::no_effect,
clippy::single_element_loop
)]
#![warn(clippy::semicolon_inside_block)]
macro_rules! m {
(()) => {
()
};
(0) => {{
0
};};
(1) => {{
1;
}};
(2) => {{
2;
}};
}
fn unit_fn_block() {
()
}
#[rustfmt::skip]
fn main() {
{ unit_fn_block() }
unsafe { unit_fn_block() }
{
unit_fn_block()
}
{ unit_fn_block() };
unsafe { unit_fn_block() };
{ unit_fn_block(); }
unsafe { unit_fn_block(); }
{ unit_fn_block(); };
unsafe { unit_fn_block(); };
{
unit_fn_block();
unit_fn_block()
};
{
unit_fn_block();
unit_fn_block();
}
{
unit_fn_block();
unit_fn_block();
};
{ m!(()) };
{ m!(()); }
{ m!(()); };
m!(0);
m!(1);
m!(2);
for _ in [()] {
unit_fn_block();
}
for _ in [()] {
unit_fn_block()
}
let _d = || {
unit_fn_block();
};
let _d = || {
unit_fn_block()
};
{ unit_fn_block(); };
unit_fn_block()
}

View File

@ -0,0 +1,18 @@
error: consider moving the `;` inside the block for consistent formatting
--> $DIR/semicolon_inside_block.rs:48:5
|
LL | / {
LL | | unit_fn_block();
LL | | unit_fn_block()
LL | | };
| |______^
|
= note: `-D clippy::semicolon-inside-block` implied by `-D warnings`
help: put the `;` here
|
LL ~ unit_fn_block();
LL ~ }
|
error: aborting due to previous error

View File

@ -0,0 +1,85 @@
//@run-rustfix
#![allow(
unused,
clippy::unused_unit,
clippy::unnecessary_operation,
clippy::no_effect,
clippy::single_element_loop
)]
#![warn(clippy::semicolon_outside_block)]
macro_rules! m {
(()) => {
()
};
(0) => {{
0
};};
(1) => {{
1;
}};
(2) => {{
2;
}};
}
fn unit_fn_block() {
()
}
#[rustfmt::skip]
fn main() {
{ unit_fn_block() }
unsafe { unit_fn_block() }
{
unit_fn_block()
}
{ unit_fn_block() };
unsafe { unit_fn_block() };
{ unit_fn_block() };
unsafe { unit_fn_block() };
{ unit_fn_block(); };
unsafe { unit_fn_block(); };
{
unit_fn_block();
unit_fn_block()
};
{
unit_fn_block();
unit_fn_block();
}
{
unit_fn_block();
unit_fn_block();
};
{ m!(()) };
{ m!(()) };
{ m!(()); };
m!(0);
m!(1);
m!(2);
for _ in [()] {
unit_fn_block();
}
for _ in [()] {
unit_fn_block()
}
let _d = || {
unit_fn_block();
};
let _d = || {
unit_fn_block()
};
{ unit_fn_block(); };
unit_fn_block()
}

View File

@ -0,0 +1,85 @@
//@run-rustfix
#![allow(
unused,
clippy::unused_unit,
clippy::unnecessary_operation,
clippy::no_effect,
clippy::single_element_loop
)]
#![warn(clippy::semicolon_outside_block)]
macro_rules! m {
(()) => {
()
};
(0) => {{
0
};};
(1) => {{
1;
}};
(2) => {{
2;
}};
}
fn unit_fn_block() {
()
}
#[rustfmt::skip]
fn main() {
{ unit_fn_block() }
unsafe { unit_fn_block() }
{
unit_fn_block()
}
{ unit_fn_block() };
unsafe { unit_fn_block() };
{ unit_fn_block(); }
unsafe { unit_fn_block(); }
{ unit_fn_block(); };
unsafe { unit_fn_block(); };
{
unit_fn_block();
unit_fn_block()
};
{
unit_fn_block();
unit_fn_block();
}
{
unit_fn_block();
unit_fn_block();
};
{ m!(()) };
{ m!(()); }
{ m!(()); };
m!(0);
m!(1);
m!(2);
for _ in [()] {
unit_fn_block();
}
for _ in [()] {
unit_fn_block()
}
let _d = || {
unit_fn_block();
};
let _d = || {
unit_fn_block()
};
{ unit_fn_block(); };
unit_fn_block()
}

View File

@ -0,0 +1,39 @@
error: consider moving the `;` outside the block for consistent formatting
--> $DIR/semicolon_outside_block.rs:42:5
|
LL | { unit_fn_block(); }
| ^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::semicolon-outside-block` implied by `-D warnings`
help: put the `;` here
|
LL - { unit_fn_block(); }
LL + { unit_fn_block() };
|
error: consider moving the `;` outside the block for consistent formatting
--> $DIR/semicolon_outside_block.rs:43:5
|
LL | unsafe { unit_fn_block(); }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: put the `;` here
|
LL - unsafe { unit_fn_block(); }
LL + unsafe { unit_fn_block() };
|
error: consider moving the `;` outside the block for consistent formatting
--> $DIR/semicolon_outside_block.rs:62:5
|
LL | { m!(()); }
| ^^^^^^^^^^^
|
help: put the `;` here
|
LL - { m!(()); }
LL + { m!(()) };
|
error: aborting due to 3 previous errors

View File

@ -37,6 +37,8 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie
missing-docs-in-crate-items
msrv
pass-by-value-size-limit
semicolon-inside-block-ignore-singleline
semicolon-outside-block-ignore-multiline
single-char-binding-names-threshold
standard-macro-braces
suppress-restriction-lint-in-const

View File

@ -0,0 +1,5 @@
#![warn(clippy::allow_attributes)]
#![feature(lint_reasons)]
#![crate_type = "proc-macro"]
fn main() {}

View File

@ -21,6 +21,13 @@ macro_rules! string_add {
};
}
#[macro_export]
macro_rules! string_lit_as_bytes {
($s:literal) => {
const C: &[u8] = $s.as_bytes();
};
}
#[macro_export]
macro_rules! mut_mut {
() => {

View File

@ -82,7 +82,7 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea
elided += 1;
// HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it.
// In order to avoid adding the dependency, get a default span from a non-existent token.
// In order to avoid adding the dependency, get a default span from a nonexistent token.
// A default span is needed to mark the code as coming from expansion.
let span = Star::default().span();

View File

@ -1,6 +1,6 @@
//@run-rustfix
#![feature(let_chains)]
#![feature(let_chains, inline_const)]
#![warn(clippy::bool_to_int_with_if)]
#![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)]
@ -79,6 +79,13 @@ fn main() {
pub const SHOULD_NOT_LINT: usize = if true { 1 } else { 0 };
// https://github.com/rust-lang/rust-clippy/issues/10452
let should_not_lint = [(); if true { 1 } else { 0 }];
let should_not_lint = const {
if true { 1 } else { 0 }
};
some_fn(a);
}

View File

@ -1,6 +1,6 @@
//@run-rustfix
#![feature(let_chains)]
#![feature(let_chains, inline_const)]
#![warn(clippy::bool_to_int_with_if)]
#![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)]
@ -111,6 +111,13 @@ fn main() {
pub const SHOULD_NOT_LINT: usize = if true { 1 } else { 0 };
// https://github.com/rust-lang/rust-clippy/issues/10452
let should_not_lint = [(); if true { 1 } else { 0 }];
let should_not_lint = const {
if true { 1 } else { 0 }
};
some_fn(a);
}

View File

@ -98,7 +98,7 @@ LL | | };
= note: `!b as i32` or `(!b).into()` can also be valid options
error: boolean to int conversion using if
--> $DIR/bool_to_int_with_if.rs:119:5
--> $DIR/bool_to_int_with_if.rs:126:5
|
LL | if a { 1 } else { 0 }
| ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)`

View File

@ -1,5 +1,6 @@
//@run-rustfix
#![warn(clippy::box_default)]
#![allow(clippy::default_constructed_unit_structs)]
#[derive(Default)]
struct ImplementsDefault;

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