Merge commit 'd5e2a7aca55ed49fc943b7a07a8eba05ab5a0079' into clippyup
This commit is contained in:
parent
58eb9964cc
commit
8df896c076
184 changed files with 3000 additions and 1635 deletions
|
@ -152,6 +152,8 @@ Current stable, released 2023-03-09
|
|||
|
||||
* `SYSROOT` and `--sysroot` can now be set at the same time
|
||||
[#10149](https://github.com/rust-lang/rust-clippy/pull/10149)
|
||||
* Fix error when providing an `array-size-threshold` in `clippy.toml`
|
||||
[#10423](https://github.com/rust-lang/rust-clippy/pull/10423)
|
||||
|
||||
## Rust 1.67
|
||||
|
||||
|
@ -186,8 +188,6 @@ Released 2023-01-26
|
|||
|
||||
### Moves and Deprecations
|
||||
|
||||
* Moved [`uninlined_format_args`] to `style` (Now warn-by-default)
|
||||
[#9865](https://github.com/rust-lang/rust-clippy/pull/9865)
|
||||
* Moved [`needless_collect`] to `nursery` (Now allow-by-default)
|
||||
[#9705](https://github.com/rust-lang/rust-clippy/pull/9705)
|
||||
* Moved [`or_fun_call`] to `nursery` (Now allow-by-default)
|
||||
|
@ -423,7 +423,7 @@ Released 2022-12-15
|
|||
[#9490](https://github.com/rust-lang/rust-clippy/pull/9490)
|
||||
* [`almost_complete_letter_range`]: No longer lints in external macros
|
||||
[#9467](https://github.com/rust-lang/rust-clippy/pull/9467)
|
||||
* [`drop_copy`]: No longer lints on idiomatic cases in match arms
|
||||
* [`drop_copy`]: No longer lints on idiomatic cases in match arms
|
||||
[#9491](https://github.com/rust-lang/rust-clippy/pull/9491)
|
||||
* [`question_mark`]: No longer lints in const context
|
||||
[#9487](https://github.com/rust-lang/rust-clippy/pull/9487)
|
||||
|
@ -4382,6 +4382,7 @@ Released 2018-09-13
|
|||
<!-- begin autogenerated links to lint list -->
|
||||
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
|
||||
[`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core
|
||||
[`allow_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes
|
||||
[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
|
||||
[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
|
||||
[`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range
|
||||
|
@ -4661,6 +4662,7 @@ Released 2018-09-13
|
|||
[`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_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
|
||||
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
|
||||
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
|
||||
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
|
||||
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
|
||||
|
@ -4985,6 +4987,7 @@ Released 2018-09-13
|
|||
[`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc
|
||||
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
|
||||
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
|
||||
[`unnecessary_struct_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_struct_initialization
|
||||
[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
|
||||
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
|
||||
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
|
||||
|
|
|
@ -50,7 +50,7 @@ a [developer guide] and is a good place to start your journey.
|
|||
All issues on Clippy are mentored, if you want help simply ask someone from the
|
||||
Clippy team directly by mentioning them in the issue or over on [Zulip]. All
|
||||
currently active team members can be found
|
||||
[here](https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust-clippy.json#L3)
|
||||
[here](https://github.com/rust-lang/rust-clippy/blob/master/triagebot.toml#L18)
|
||||
|
||||
Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy
|
||||
issues. You can use `@rustbot claim` to assign the issue to yourself.
|
||||
|
|
|
@ -519,6 +519,7 @@ for the generic parameters for determining interior mutability
|
|||
**Default Value:** `["bytes::Bytes"]` (`Vec<String>`)
|
||||
|
||||
* [mutable_key_type](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type)
|
||||
* [ifs_same_cond](https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond)
|
||||
|
||||
|
||||
### allow-mixed-uninlined-format-args
|
||||
|
|
71
clippy_lints/src/allow_attributes.rs
Normal file
71
clippy_lints/src/allow_attributes.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
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_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// Detects uses of the `#[allow]` attribute and suggests replacing it with
|
||||
/// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
|
||||
///
|
||||
/// The expect attribute is still unstable and requires the `lint_reasons`
|
||||
/// on nightly. It can be enabled by adding `#![feature(lint_reasons)]` to
|
||||
/// the crate root.
|
||||
///
|
||||
/// This lint only warns outer attributes (`#[allow]`), as inner attributes
|
||||
/// (`#![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.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// #[allow(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// #![feature(lint_reasons)]
|
||||
/// #[expect(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.69.0"]
|
||||
pub ALLOW_ATTRIBUTES,
|
||||
restriction,
|
||||
"`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings."
|
||||
}
|
||||
|
||||
declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTES]);
|
||||
|
||||
impl LateLintPass<'_> for AllowAttribute {
|
||||
// Separate each crate's features.
|
||||
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
|
||||
if_chain! {
|
||||
if cx.tcx.features().lint_reasons;
|
||||
if let AttrStyle::Outer = attr.style;
|
||||
if let Some(ident) = attr.ident();
|
||||
if ident.name == rustc_span::symbol::sym::allow;
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ALLOW_ATTRIBUTES,
|
||||
ident.span,
|
||||
"#[allow] attribute found",
|
||||
"replace it with",
|
||||
"expect".into(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -495,18 +495,19 @@ struct NotSimplificationVisitor<'a, 'tcx> {
|
|||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind {
|
||||
if let Some(suggestion) = simplify_not(self.cx, inner) {
|
||||
span_lint_and_sugg(
|
||||
self.cx,
|
||||
NONMINIMAL_BOOL,
|
||||
expr.span,
|
||||
"this boolean expression can be simplified",
|
||||
"try",
|
||||
suggestion,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind &&
|
||||
!inner.span.from_expansion() &&
|
||||
let Some(suggestion) = simplify_not(self.cx, inner)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
self.cx,
|
||||
NONMINIMAL_BOOL,
|
||||
expr.span,
|
||||
"this boolean expression can be simplified",
|
||||
"try",
|
||||
suggestion,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
walk_expr(self, expr);
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
|
||||
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
|
||||
use clippy_utils::ty::needs_ordered_drop;
|
||||
use clippy_utils::ty::{is_interior_mut_ty, needs_ordered_drop};
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{
|
||||
capture_local_usage, eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause,
|
||||
is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
|
||||
capture_local_usage, def_path_def_ids, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt,
|
||||
if_sequence, is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
|
||||
};
|
||||
use core::iter;
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefIdSet;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_middle::query::Key;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::hygiene::walk_chain;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{BytePos, Span, Symbol};
|
||||
|
@ -159,7 +161,21 @@ declare_clippy_lint! {
|
|||
"`if` statement with shared code in all blocks"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CopyAndPaste => [
|
||||
pub struct CopyAndPaste {
|
||||
ignore_interior_mutability: Vec<String>,
|
||||
ignored_ty_ids: DefIdSet,
|
||||
}
|
||||
|
||||
impl CopyAndPaste {
|
||||
pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
|
||||
Self {
|
||||
ignore_interior_mutability,
|
||||
ignored_ty_ids: DefIdSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CopyAndPaste => [
|
||||
IFS_SAME_COND,
|
||||
SAME_FUNCTIONS_IN_IF_CONDITION,
|
||||
IF_SAME_THEN_ELSE,
|
||||
|
@ -167,10 +183,18 @@ declare_lint_pass!(CopyAndPaste => [
|
|||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
||||
for ignored_ty in &self.ignore_interior_mutability {
|
||||
let path: Vec<&str> = ignored_ty.split("::").collect();
|
||||
for id in def_path_def_ids(cx, path.as_slice()) {
|
||||
self.ignored_ty_ids.insert(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) {
|
||||
let (conds, blocks) = if_sequence(expr);
|
||||
lint_same_cond(cx, &conds);
|
||||
lint_same_cond(cx, &conds, &self.ignored_ty_ids);
|
||||
lint_same_fns_in_if_cond(cx, &conds);
|
||||
let all_same =
|
||||
!is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks);
|
||||
|
@ -547,9 +571,39 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo
|
|||
})
|
||||
}
|
||||
|
||||
fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignored_ty_ids: &DefIdSet) -> bool {
|
||||
let caller_ty = cx.typeck_results().expr_ty(caller_expr);
|
||||
// Check if given type has inner mutability and was not set to ignored by the configuration
|
||||
let is_inner_mut_ty = is_interior_mut_ty(cx, caller_ty)
|
||||
&& !matches!(caller_ty.ty_adt_id(), Some(adt_id) if ignored_ty_ids.contains(&adt_id));
|
||||
|
||||
is_inner_mut_ty
|
||||
|| caller_ty.is_mutable_ptr()
|
||||
// `find_binding_init` will return the binding iff its not mutable
|
||||
|| path_to_local(caller_expr)
|
||||
.and_then(|hid| find_binding_init(cx, hid))
|
||||
.is_none()
|
||||
}
|
||||
|
||||
/// Implementation of `IFS_SAME_COND`.
|
||||
fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
|
||||
for (i, j) in search_same(conds, |e| hash_expr(cx, e), |lhs, rhs| eq_expr_value(cx, lhs, rhs)) {
|
||||
fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &DefIdSet) {
|
||||
for (i, j) in search_same(
|
||||
conds,
|
||||
|e| hash_expr(cx, e),
|
||||
|lhs, rhs| {
|
||||
// Ignore eq_expr side effects iff one of the expressin 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) {
|
||||
false
|
||||
} else {
|
||||
SpanlessEq::new(cx).eq_expr(lhs, rhs)
|
||||
}
|
||||
} else {
|
||||
eq_expr_value(cx, lhs, rhs)
|
||||
}
|
||||
},
|
||||
) {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
IFS_SAME_COND,
|
||||
|
|
|
@ -35,6 +35,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO,
|
||||
#[cfg(feature = "internal")]
|
||||
crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO,
|
||||
crate::allow_attributes::ALLOW_ATTRIBUTES_INFO,
|
||||
crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
|
||||
crate::approx_const::APPROX_CONSTANT_INFO,
|
||||
crate::as_conversions::AS_CONVERSIONS_INFO,
|
||||
|
@ -262,6 +263,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::manual_clamp::MANUAL_CLAMP_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,
|
||||
crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
|
||||
crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO,
|
||||
crate::manual_retain::MANUAL_RETAIN_INFO,
|
||||
|
@ -616,6 +618,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO,
|
||||
crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
|
||||
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
|
||||
crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,
|
||||
crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO,
|
||||
crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO,
|
||||
crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::{has_drop, is_copy};
|
||||
use clippy_utils::{
|
||||
any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro, match_def_path, paths,
|
||||
|
@ -160,6 +160,8 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
}
|
||||
};
|
||||
|
||||
let init_ctxt = local.span.ctxt();
|
||||
|
||||
// find all "later statement"'s where the fields of the binding set as
|
||||
// Default::default() get reassigned, unless the reassignment refers to the original binding
|
||||
let mut first_assign = None;
|
||||
|
@ -169,7 +171,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
// find out if and which field was set by this `consecutive_statement`
|
||||
if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) {
|
||||
// interrupt and cancel lint if assign_rhs references the original binding
|
||||
if contains_name(binding_name, assign_rhs, cx) {
|
||||
if contains_name(binding_name, assign_rhs, cx) || init_ctxt != consecutive_statement.span.ctxt() {
|
||||
cancel_lint = true;
|
||||
break;
|
||||
}
|
||||
|
@ -204,11 +206,12 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
.iter()
|
||||
.all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name));
|
||||
|
||||
let mut app = Applicability::Unspecified;
|
||||
let field_list = assigned_fields
|
||||
.into_iter()
|
||||
.map(|(field, rhs)| {
|
||||
// extract and store the assigned value for help message
|
||||
let value_snippet = snippet_with_macro_callsite(cx, rhs.span, "..");
|
||||
let value_snippet = snippet_with_context(cx, rhs.span, init_ctxt, "..", &mut app).0;
|
||||
format!("{field}: {value_snippet}")
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
|
|
|
@ -24,8 +24,8 @@ use rustc_span::sym;
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for deriving `Hash` but implementing `PartialEq`
|
||||
/// explicitly or vice versa.
|
||||
/// Lints against manual `PartialEq` implementations for types with a derived `Hash`
|
||||
/// implementation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The implementation of these traits must agree (for
|
||||
|
@ -54,8 +54,8 @@ declare_clippy_lint! {
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for deriving `Ord` but implementing `PartialOrd`
|
||||
/// explicitly or vice versa.
|
||||
/// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord`
|
||||
/// or `PartialOrd` implementation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The implementation of these traits must agree (for
|
||||
|
|
|
@ -50,10 +50,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
|
|||
let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
|
||||
if let Some(attr) = attr {
|
||||
check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr);
|
||||
} else if is_public
|
||||
&& !is_proc_macro(attrs)
|
||||
&& trait_ref_of_method(cx, item.owner_id.def_id).is_none()
|
||||
{
|
||||
} else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none() {
|
||||
check_must_use_candidate(
|
||||
cx,
|
||||
sig.decl,
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
@ -72,21 +74,20 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
|||
return;
|
||||
}
|
||||
|
||||
let ctxt = expr.span.ctxt();
|
||||
|
||||
if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
|
||||
&& let ExprKind::Block(then_block, _) = then.kind
|
||||
&& let Some(then_expr) = then_block.expr
|
||||
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
|
||||
&& then_expr.span.ctxt() == ctxt
|
||||
&& is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome)
|
||||
&& is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone)
|
||||
&& !stmts_contains_early_return(then_block.stmts)
|
||||
{
|
||||
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
|
||||
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
|
||||
format!("({cond_snip})")
|
||||
} else {
|
||||
cond_snip.into_owned()
|
||||
};
|
||||
let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
|
||||
let mut app = Applicability::Unspecified;
|
||||
let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app).maybe_par().to_string();
|
||||
let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
|
||||
let mut method_body = if then_block.stmts.is_empty() {
|
||||
arg_snip.into_owned()
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_hir::*;
|
||||
use rustc_hir::{Local, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
|
|
@ -67,6 +67,7 @@ mod declared_lints;
|
|||
mod renamed_lints;
|
||||
|
||||
// begin lints modules, do not remove this comment, it’s used in `update_lints`
|
||||
mod allow_attributes;
|
||||
mod almost_complete_range;
|
||||
mod approx_const;
|
||||
mod as_conversions;
|
||||
|
@ -179,6 +180,7 @@ mod manual_bits;
|
|||
mod manual_clamp;
|
||||
mod manual_is_ascii_check;
|
||||
mod manual_let_else;
|
||||
mod manual_main_separator_str;
|
||||
mod manual_non_exhaustive;
|
||||
mod manual_rem_euclid;
|
||||
mod manual_retain;
|
||||
|
@ -300,6 +302,7 @@ mod unit_types;
|
|||
mod unnamed_address;
|
||||
mod unnecessary_owned_empty_strings;
|
||||
mod unnecessary_self_imports;
|
||||
mod unnecessary_struct_initialization;
|
||||
mod unnecessary_wraps;
|
||||
mod unnested_or_patterns;
|
||||
mod unsafe_removed_from_name;
|
||||
|
@ -656,7 +659,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum));
|
||||
store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
|
||||
store.register_late_pass(|_| Box::new(regex::Regex));
|
||||
store.register_late_pass(|_| Box::new(copies::CopyAndPaste));
|
||||
let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
|
||||
store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone())));
|
||||
store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));
|
||||
store.register_late_pass(|_| Box::new(format::UselessFormat));
|
||||
store.register_late_pass(|_| Box::new(swap::Swap));
|
||||
|
@ -933,6 +937,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
|
||||
store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock));
|
||||
store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
|
||||
store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute));
|
||||
store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv())));
|
||||
store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
use super::SAME_ITEM_PUSH;
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::path_to_local;
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::SyntaxContext;
|
||||
use std::iter::Iterator;
|
||||
|
||||
/// Detects for loop pushing the same item into a Vec
|
||||
|
@ -20,9 +22,10 @@ pub(super) fn check<'tcx>(
|
|||
body: &'tcx Expr<'_>,
|
||||
_: &'tcx Expr<'_>,
|
||||
) {
|
||||
fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) {
|
||||
let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
|
||||
let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
|
||||
fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext) {
|
||||
let mut app = Applicability::Unspecified;
|
||||
let vec_str = snippet_with_context(cx, vec.span, ctxt, "", &mut app).0;
|
||||
let item_str = snippet_with_context(cx, pushed_item.span, ctxt, "", &mut app).0;
|
||||
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
|
@ -43,7 +46,7 @@ pub(super) fn check<'tcx>(
|
|||
walk_expr(&mut same_item_push_visitor, body);
|
||||
if_chain! {
|
||||
if same_item_push_visitor.should_lint();
|
||||
if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push;
|
||||
if let Some((vec, pushed_item, ctxt)) = same_item_push_visitor.vec_push;
|
||||
let vec_ty = cx.typeck_results().expr_ty(vec);
|
||||
let ty = vec_ty.walk().nth(1).unwrap().expect_ty();
|
||||
if cx
|
||||
|
@ -69,11 +72,11 @@ pub(super) fn check<'tcx>(
|
|||
then {
|
||||
match init.kind {
|
||||
// immutable bindings that are initialized with literal
|
||||
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
|
||||
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt),
|
||||
// immutable bindings that are initialized with constant
|
||||
ExprKind::Path(ref path) => {
|
||||
if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) {
|
||||
emit_lint(cx, vec, pushed_item);
|
||||
emit_lint(cx, vec, pushed_item, ctxt);
|
||||
}
|
||||
}
|
||||
_ => {},
|
||||
|
@ -82,11 +85,11 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
},
|
||||
// constant
|
||||
Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item),
|
||||
Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt),
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
|
||||
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +101,7 @@ struct SameItemPushVisitor<'a, 'tcx> {
|
|||
non_deterministic_expr: bool,
|
||||
multiple_pushes: bool,
|
||||
// this field holds the last vec push operation visited, which should be the only push seen
|
||||
vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>,
|
||||
vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)>,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
used_locals: FxHashSet<HirId>,
|
||||
}
|
||||
|
@ -118,7 +121,7 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> {
|
|||
if_chain! {
|
||||
if !self.non_deterministic_expr;
|
||||
if !self.multiple_pushes;
|
||||
if let Some((vec, _)) = self.vec_push;
|
||||
if let Some((vec, _, _)) = self.vec_push;
|
||||
if let Some(hir_id) = path_to_local(vec);
|
||||
then {
|
||||
!self.used_locals.contains(&hir_id)
|
||||
|
@ -173,7 +176,10 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
|
|||
|
||||
// Given some statement, determine if that statement is a push on a Vec. If it is, return
|
||||
// the Vec being pushed into and the item being pushed
|
||||
fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
|
||||
fn get_vec_push<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
stmt: &'tcx Stmt<'_>,
|
||||
) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)> {
|
||||
if_chain! {
|
||||
// Extract method being called
|
||||
if let StmtKind::Semi(semi_stmt) = &stmt.kind;
|
||||
|
@ -184,7 +190,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&
|
|||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec);
|
||||
if path.ident.name.as_str() == "push";
|
||||
then {
|
||||
return Some((self_expr, pushed_item))
|
||||
return Some((self_expr, pushed_item, semi_stmt.span.ctxt()))
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound,
|
||||
ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
|
||||
ImplItem, Item, ItemKind, LifetimeName, Node, Term, TraitRef, Ty, TyKind, TypeBindingKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
|||
decl: &'tcx FnDecl<'_>,
|
||||
body: &'tcx Body<'_>,
|
||||
span: Span,
|
||||
_: LocalDefId,
|
||||
def_id: LocalDefId,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(header) = kind.header();
|
||||
|
@ -59,6 +59,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
|||
if let ExprKind::Block(block, _) = body.value.kind;
|
||||
if block.stmts.is_empty();
|
||||
if let Some(closure_body) = desugared_async_block(cx, block);
|
||||
if let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) =
|
||||
cx.tcx.hir().get_by_def_id(def_id);
|
||||
then {
|
||||
let header_span = span.with_hi(ret_ty.span.hi());
|
||||
|
||||
|
@ -69,15 +71,22 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
|||
"this function can be simplified using the `async fn` syntax",
|
||||
|diag| {
|
||||
if_chain! {
|
||||
if let Some(vis_snip) = snippet_opt(cx, *vis_span);
|
||||
if let Some(header_snip) = snippet_opt(cx, header_span);
|
||||
if let Some(ret_pos) = position_before_rarrow(&header_snip);
|
||||
if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output);
|
||||
then {
|
||||
let header_snip = if vis_snip.is_empty() {
|
||||
format!("async {}", &header_snip[..ret_pos])
|
||||
} else {
|
||||
format!("{} async {}", vis_snip, &header_snip[vis_snip.len() + 1..ret_pos])
|
||||
};
|
||||
|
||||
let help = format!("make the function `async` and {ret_sugg}");
|
||||
diag.span_suggestion(
|
||||
header_span,
|
||||
help,
|
||||
format!("async {}{ret_snip}", &header_snip[..ret_pos]),
|
||||
format!("{header_snip}{ret_snip}"),
|
||||
Applicability::MachineApplicable
|
||||
);
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@ use clippy_utils::ty::implements_trait;
|
|||
use clippy_utils::visitors::is_const_evaluatable;
|
||||
use clippy_utils::MaybePath;
|
||||
use clippy_utils::{
|
||||
eq_expr_value, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
|
||||
eq_expr_value, in_constant, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks,
|
||||
peel_blocks_with_stmt,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -117,7 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp {
|
|||
if !self.msrv.meets(msrvs::CLAMP) {
|
||||
return;
|
||||
}
|
||||
if !expr.span.from_expansion() {
|
||||
if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) {
|
||||
let suggestion = is_if_elseif_else_pattern(cx, expr)
|
||||
.or_else(|| is_max_min_pattern(cx, expr))
|
||||
.or_else(|| is_call_max_min_pattern(cx, expr))
|
||||
|
@ -130,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp {
|
|||
}
|
||||
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
|
||||
if !self.msrv.meets(msrvs::CLAMP) {
|
||||
if !self.msrv.meets(msrvs::CLAMP) || in_constant(cx, block.hir_id) {
|
||||
return;
|
||||
}
|
||||
for suggestion in is_two_if_pattern(cx, block) {
|
||||
|
|
74
clippy_lints/src/manual_main_separator_str.rs
Normal file
74
clippy_lints/src/manual_main_separator_str.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::{is_trait_method, match_def_path, paths, peel_hir_expr_refs};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Expr, ExprKind, Mutability, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for references on `std::path::MAIN_SEPARATOR.to_string()` used
|
||||
/// to build a `&str`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// There exists a `std::path::MAIN_SEPARATOR_STR` which does not require
|
||||
/// an extra memory allocation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let s: &str = &std::path::MAIN_SEPARATOR.to_string();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let s: &str = std::path::MAIN_SEPARATOR_STR;
|
||||
/// ```
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub MANUAL_MAIN_SEPARATOR_STR,
|
||||
complexity,
|
||||
"`&std::path::MAIN_SEPARATOR.to_string()` can be replaced by `std::path::MAIN_SEPARATOR_STR`"
|
||||
}
|
||||
|
||||
pub struct ManualMainSeparatorStr {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl ManualMainSeparatorStr {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ManualMainSeparatorStr => [MANUAL_MAIN_SEPARATOR_STR]);
|
||||
|
||||
impl LateLintPass<'_> for ManualMainSeparatorStr {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR) &&
|
||||
let (target, _) = peel_hir_expr_refs(expr) &&
|
||||
is_trait_method(cx, target, sym::ToString) &&
|
||||
let ExprKind::MethodCall(path, receiver, &[], _) = target.kind &&
|
||||
path.ident.name == sym::to_string &&
|
||||
let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind &&
|
||||
let Res::Def(DefKind::Const, receiver_def_id) = path.res &&
|
||||
match_def_path(cx, receiver_def_id, &paths::PATH_MAIN_SEPARATOR) &&
|
||||
let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() &&
|
||||
ty.is_str()
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_MAIN_SEPARATOR_STR,
|
||||
expr.span,
|
||||
"taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`",
|
||||
"replace with",
|
||||
"std::path::MAIN_SEPARATOR_STR".to_owned(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
|
@ -32,14 +32,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee:
|
|||
let reindented_or_body =
|
||||
reindent_multiline(or_body_snippet.into(), true, Some(indent));
|
||||
|
||||
let suggestion = if scrutinee.span.from_expansion() {
|
||||
// we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)`
|
||||
sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..")
|
||||
}
|
||||
else {
|
||||
sugg::Sugg::hir(cx, scrutinee, "..").maybe_par()
|
||||
};
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par();
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_UNWRAP_OR, expr.span,
|
||||
|
@ -48,7 +42,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee:
|
|||
format!(
|
||||
"{suggestion}.unwrap_or({reindented_or_body})",
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@ use rustc_middle::ty;
|
|||
|
||||
use super::MATCH_BOOL;
|
||||
|
||||
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
|
||||
pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
|
||||
// Type of expression is `bool`.
|
||||
if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool {
|
||||
if *cx.typeck_results().expr_ty(scrutinee).kind() == ty::Bool {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MATCH_BOOL,
|
||||
|
@ -36,24 +36,26 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
|
|||
};
|
||||
|
||||
if let Some((true_expr, false_expr)) = exprs {
|
||||
let mut app = Applicability::HasPlaceholders;
|
||||
let ctxt = expr.span.ctxt();
|
||||
let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
|
||||
(false, false) => Some(format!(
|
||||
"if {} {} else {}",
|
||||
snippet(cx, ex.span, "b"),
|
||||
expr_block(cx, true_expr, None, "..", Some(expr.span)),
|
||||
expr_block(cx, false_expr, None, "..", Some(expr.span))
|
||||
snippet(cx, scrutinee.span, "b"),
|
||||
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app),
|
||||
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
|
||||
)),
|
||||
(false, true) => Some(format!(
|
||||
"if {} {}",
|
||||
snippet(cx, ex.span, "b"),
|
||||
expr_block(cx, true_expr, None, "..", Some(expr.span))
|
||||
snippet(cx, scrutinee.span, "b"),
|
||||
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app)
|
||||
)),
|
||||
(true, false) => {
|
||||
let test = Sugg::hir(cx, ex, "..");
|
||||
let test = Sugg::hir(cx, scrutinee, "..");
|
||||
Some(format!(
|
||||
"if {} {}",
|
||||
!test,
|
||||
expr_block(cx, false_expr, None, "..", Some(expr.span))
|
||||
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
|
||||
))
|
||||
},
|
||||
(true, true) => None,
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::source::{snippet, walk_span_to_context};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use core::iter::once;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::MATCH_REF_PATS;
|
||||
|
||||
pub(crate) fn check<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
|
||||
pub(crate) fn check<'a, 'b, I>(cx: &LateContext<'_>, scrutinee: &Expr<'_>, pats: I, expr: &Expr<'_>)
|
||||
where
|
||||
'b: 'a,
|
||||
I: Clone + Iterator<Item = &'a Pat<'b>>,
|
||||
|
@ -17,13 +18,28 @@ where
|
|||
}
|
||||
|
||||
let (first_sugg, msg, title);
|
||||
let span = ex.span.source_callsite();
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
|
||||
first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
|
||||
let ctxt = expr.span.ctxt();
|
||||
let mut app = Applicability::Unspecified;
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = scrutinee.kind {
|
||||
if scrutinee.span.ctxt() != ctxt {
|
||||
return;
|
||||
}
|
||||
first_sugg = once((
|
||||
scrutinee.span,
|
||||
Sugg::hir_with_context(cx, inner, ctxt, "..", &mut app).to_string(),
|
||||
));
|
||||
msg = "try";
|
||||
title = "you don't need to add `&` to both the expression and the patterns";
|
||||
} else {
|
||||
first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
|
||||
let Some(span) = walk_span_to_context(scrutinee.span, ctxt) else {
|
||||
return;
|
||||
};
|
||||
first_sugg = once((
|
||||
span,
|
||||
Sugg::hir_with_context(cx, scrutinee, ctxt, "..", &mut app)
|
||||
.deref()
|
||||
.to_string(),
|
||||
));
|
||||
msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
|
||||
title = "you don't need to add `&` to all patterns";
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::macros::HirNode;
|
||||
use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_applicability};
|
||||
use clippy_utils::{get_parent_expr, is_refutable, peel_blocks};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind, StmtKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
|
||||
|
@ -24,21 +23,30 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
|
|||
let matched_vars = ex.span;
|
||||
let bind_names = arms[0].pat.span;
|
||||
let match_body = peel_blocks(arms[0].body);
|
||||
let mut snippet_body = if match_body.span.from_expansion() {
|
||||
Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
|
||||
} else {
|
||||
snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string()
|
||||
};
|
||||
let mut app = Applicability::MaybeIncorrect;
|
||||
let mut snippet_body = snippet_block_with_context(
|
||||
cx,
|
||||
match_body.span,
|
||||
arms[0].span.ctxt(),
|
||||
"..",
|
||||
Some(expr.span),
|
||||
&mut app,
|
||||
)
|
||||
.0
|
||||
.to_string();
|
||||
|
||||
// Do we need to add ';' to suggestion ?
|
||||
if let ExprKind::Block(block, _) = match_body.kind {
|
||||
// macro + expr_ty(body) == ()
|
||||
if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
|
||||
snippet_body.push(';');
|
||||
if let Node::Stmt(stmt) = cx.tcx.hir().get_parent(expr.hir_id)
|
||||
&& let StmtKind::Expr(_) = stmt.kind
|
||||
&& match match_body.kind {
|
||||
// We don't need to add a ; to blocks, unless that block is from a macro expansion
|
||||
ExprKind::Block(block, _) => block.span.from_expansion(),
|
||||
_ => true,
|
||||
}
|
||||
{
|
||||
snippet_body.push(';');
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
match arms[0].pat.kind {
|
||||
PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
|
||||
let (target_span, sugg) = match opt_parent_assign_span(cx, ex) {
|
||||
|
@ -48,7 +56,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
|
|||
(ex, expr),
|
||||
(bind_names, matched_vars),
|
||||
&snippet_body,
|
||||
&mut applicability,
|
||||
&mut app,
|
||||
Some(span),
|
||||
true,
|
||||
);
|
||||
|
@ -60,7 +68,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
|
|||
"this assignment could be simplified",
|
||||
"consider removing the `match` expression",
|
||||
sugg,
|
||||
applicability,
|
||||
app,
|
||||
);
|
||||
|
||||
return;
|
||||
|
@ -69,10 +77,10 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
|
|||
span,
|
||||
format!(
|
||||
"let {} = {};\n{}let {} = {snippet_body};",
|
||||
snippet_with_applicability(cx, bind_names, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, bind_names, "..", &mut app),
|
||||
snippet_with_applicability(cx, matched_vars, "..", &mut app),
|
||||
" ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
|
||||
snippet_with_applicability(cx, pat_span, "..", &mut applicability)
|
||||
snippet_with_applicability(cx, pat_span, "..", &mut app)
|
||||
),
|
||||
),
|
||||
None => {
|
||||
|
@ -81,7 +89,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
|
|||
(ex, expr),
|
||||
(bind_names, matched_vars),
|
||||
&snippet_body,
|
||||
&mut applicability,
|
||||
&mut app,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
|
@ -96,7 +104,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
|
|||
"this match could be written as a `let` statement",
|
||||
"consider using a `let` statement",
|
||||
sugg,
|
||||
applicability,
|
||||
app,
|
||||
);
|
||||
},
|
||||
PatKind::Wild => {
|
||||
|
@ -106,7 +114,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
|
|||
(ex, expr),
|
||||
(bind_names, matched_vars),
|
||||
&snippet_body,
|
||||
&mut applicability,
|
||||
&mut app,
|
||||
None,
|
||||
false,
|
||||
);
|
||||
|
@ -118,7 +126,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
|
|||
"this match could be replaced by its scrutinee and body",
|
||||
"consider using the scrutinee and body instead",
|
||||
sugg,
|
||||
applicability,
|
||||
app,
|
||||
);
|
||||
} else {
|
||||
span_lint_and_sugg(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::REDUNDANT_PATTERN_MATCHING;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::source::{snippet, walk_span_to_context};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
|
||||
use clippy_utils::visitors::any_temporaries_need_ordered_drop;
|
||||
|
@ -150,22 +150,25 @@ fn find_sugg_for_if_let<'tcx>(
|
|||
// if/while let ... = ... { ... }
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
let expr_span = expr.span;
|
||||
let ctxt = expr.span.ctxt();
|
||||
|
||||
// if/while let ... = ... { ... }
|
||||
// ^^^
|
||||
let op_span = result_expr.span.source_callsite();
|
||||
// ^^^
|
||||
let Some(res_span) = walk_span_to_context(result_expr.span.source_callsite(), ctxt) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// if/while let ... = ... { ... }
|
||||
// ^^^^^^^^^^^^^^^^^^^
|
||||
let span = expr_span.until(op_span.shrink_to_hi());
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^
|
||||
let span = expr_span.until(res_span.shrink_to_hi());
|
||||
|
||||
let app = if needs_drop {
|
||||
let mut app = if needs_drop {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
|
||||
let sugg = Sugg::hir_with_macro_callsite(cx, result_expr, "_")
|
||||
let sugg = Sugg::hir_with_context(cx, result_expr, ctxt, "_", &mut app)
|
||||
.maybe_par()
|
||||
.to_string();
|
||||
|
||||
|
|
|
@ -67,8 +67,10 @@ fn report_single_pattern(
|
|||
els: Option<&Expr<'_>>,
|
||||
) {
|
||||
let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
|
||||
let ctxt = expr.span.ctxt();
|
||||
let mut app = Applicability::HasPlaceholders;
|
||||
let els_str = els.map_or(String::new(), |els| {
|
||||
format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
|
||||
format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
|
||||
});
|
||||
|
||||
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
|
||||
|
@ -103,7 +105,7 @@ fn report_single_pattern(
|
|||
// PartialEq for different reference counts may not exist.
|
||||
"&".repeat(ref_count_diff),
|
||||
snippet(cx, arms[0].pat.span, ".."),
|
||||
expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
|
||||
expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
|
||||
);
|
||||
(msg, sugg)
|
||||
} else {
|
||||
|
@ -112,21 +114,13 @@ fn report_single_pattern(
|
|||
"if let {} = {} {}{els_str}",
|
||||
snippet(cx, arms[0].pat.span, ".."),
|
||||
snippet(cx, ex.span, ".."),
|
||||
expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
|
||||
expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
|
||||
);
|
||||
(msg, sugg)
|
||||
}
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
msg,
|
||||
"try this",
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
span_lint_and_sugg(cx, lint, expr.span, msg, "try this", sugg, app);
|
||||
}
|
||||
|
||||
fn check_opt_like<'a>(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::{contains_return, BIND_INSTEAD_OF_MAP};
|
||||
use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::{snippet, snippet_with_macro_callsite};
|
||||
use clippy_utils::source::{snippet, snippet_with_context};
|
||||
use clippy_utils::{peel_blocks, visitors::find_all_ret_expressions};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -76,11 +76,8 @@ pub(crate) trait BindInsteadOfMap {
|
|||
if !contains_return(inner_expr);
|
||||
if let Some(msg) = Self::lint_msg(cx);
|
||||
then {
|
||||
let some_inner_snip = if inner_expr.span.from_expansion() {
|
||||
snippet_with_macro_callsite(cx, inner_expr.span, "_")
|
||||
} else {
|
||||
snippet(cx, inner_expr.span, "_")
|
||||
};
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let some_inner_snip = snippet_with_context(cx, inner_expr.span, closure_expr.span.ctxt(), "_", &mut app).0;
|
||||
|
||||
let closure_args_snip = snippet(cx, closure_args_span, "..");
|
||||
let option_snip = snippet(cx, recv.span, "..");
|
||||
|
@ -92,7 +89,7 @@ pub(crate) trait BindInsteadOfMap {
|
|||
&msg,
|
||||
"try this",
|
||||
note,
|
||||
Applicability::MachineApplicable,
|
||||
app,
|
||||
);
|
||||
true
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::paths;
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, match_type};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
|
@ -33,7 +33,9 @@ pub(super) fn check(
|
|||
return;
|
||||
};
|
||||
|
||||
let snippet = snippet_with_macro_callsite(cx, receiver.span, "..");
|
||||
// Sometimes unnecessary ::<_> after Rc/Arc/Weak
|
||||
let mut app = Applicability::Unspecified;
|
||||
let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0;
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -42,7 +44,7 @@ pub(super) fn check(
|
|||
"using `.clone()` on a ref-counted pointer",
|
||||
"try this",
|
||||
format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)),
|
||||
Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
|
||||
use clippy_utils::source::{snippet, snippet_with_macro_callsite};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{contains_return, is_trait_item, last_path_segment};
|
||||
use if_chain::if_chain;
|
||||
|
@ -9,7 +9,6 @@ use rustc_hir as hir;
|
|||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{kw, sym, Symbol};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::OR_FUN_CALL;
|
||||
|
||||
|
@ -111,37 +110,24 @@ pub(super) fn check<'tcx>(
|
|||
if poss.contains(&name);
|
||||
|
||||
then {
|
||||
let ctxt = span.ctxt();
|
||||
let mut app = Applicability::HasPlaceholders;
|
||||
let sugg = {
|
||||
let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
|
||||
(false, Some(fun_span)) => (fun_span, false),
|
||||
_ => (arg.span, true),
|
||||
};
|
||||
|
||||
let format_span = |span: Span| {
|
||||
let not_macro_argument_snippet = snippet_with_macro_callsite(cx, span, "..");
|
||||
let snip = if not_macro_argument_snippet == "vec![]" {
|
||||
let macro_expanded_snipped = snippet(cx, snippet_span, "..");
|
||||
match macro_expanded_snipped.strip_prefix("$crate::vec::") {
|
||||
Some(stripped) => Cow::Owned(stripped.to_owned()),
|
||||
None => macro_expanded_snipped,
|
||||
}
|
||||
} else {
|
||||
not_macro_argument_snippet
|
||||
};
|
||||
|
||||
snip.to_string()
|
||||
};
|
||||
|
||||
let snip = format_span(snippet_span);
|
||||
let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0;
|
||||
let snip = if use_lambda {
|
||||
let l_arg = if fn_has_arguments { "_" } else { "" };
|
||||
format!("|{l_arg}| {snip}")
|
||||
} else {
|
||||
snip
|
||||
snip.into_owned()
|
||||
};
|
||||
|
||||
if let Some(f) = second_arg {
|
||||
let f = format_span(f.span);
|
||||
let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0;
|
||||
format!("{snip}, {f}")
|
||||
} else {
|
||||
snip
|
||||
|
@ -155,7 +141,7 @@ pub(super) fn check<'tcx>(
|
|||
&format!("use of `{name}` followed by a function call"),
|
||||
"try this",
|
||||
format!("{name}_{suffix}({sugg})"),
|
||||
Applicability::HasPlaceholders,
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then};
|
||||
use clippy_utils::source::{snippet, snippet_opt};
|
||||
use clippy_utils::source::{snippet, snippet_opt, snippet_with_context};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
|
@ -181,20 +181,17 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
|
|||
if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind;
|
||||
if let Some(init) = local.init;
|
||||
then {
|
||||
// use the macro callsite when the init span (but not the whole local span)
|
||||
// comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];`
|
||||
let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() {
|
||||
Sugg::hir_with_macro_callsite(cx, init, "..")
|
||||
} else {
|
||||
Sugg::hir(cx, init, "..")
|
||||
};
|
||||
let ctxt = local.span.ctxt();
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app);
|
||||
let (mutopt, initref) = if mutabl == Mutability::Mut {
|
||||
("mut ", sugg_init.mut_addr())
|
||||
} else {
|
||||
("", sugg_init.addr())
|
||||
};
|
||||
let tyopt = if let Some(ty) = local.ty {
|
||||
format!(": &{mutopt}{ty}", ty=snippet(cx, ty.span, ".."))
|
||||
let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0;
|
||||
format!(": &{mutopt}{ty_snip}")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
@ -212,7 +209,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
|
|||
"let {name}{tyopt} = {initref};",
|
||||
name=snippet(cx, name.span, ".."),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
app,
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::is_interior_mut_ty;
|
||||
use clippy_utils::{def_path_def_ids, trait_ref_of_method};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty};
|
||||
use rustc_middle::query::Key;
|
||||
use rustc_middle::ty::{Adt, Ty};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::source_map::Span;
|
||||
|
@ -153,53 +154,18 @@ impl MutableKeyType {
|
|||
let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet]
|
||||
.iter()
|
||||
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did()));
|
||||
if is_keyed_type && self.is_interior_mutable_type(cx, substs.type_at(0)) {
|
||||
if !is_keyed_type {
|
||||
return;
|
||||
}
|
||||
|
||||
let subst_ty = substs.type_at(0);
|
||||
// Determines if a type contains interior mutability which would affect its implementation of
|
||||
// [`Hash`] or [`Ord`].
|
||||
if is_interior_mut_ty(cx, subst_ty)
|
||||
&& !matches!(subst_ty.ty_adt_id(), Some(adt_id) if self.ignore_mut_def_ids.contains(&adt_id))
|
||||
{
|
||||
span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if a type contains interior mutability which would affect its implementation of
|
||||
/// [`Hash`] or [`Ord`].
|
||||
fn is_interior_mutable_type<'tcx>(&self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
match *ty.kind() {
|
||||
Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || self.is_interior_mutable_type(cx, inner_ty),
|
||||
Slice(inner_ty) => self.is_interior_mutable_type(cx, inner_ty),
|
||||
Array(inner_ty, size) => {
|
||||
size.try_eval_target_usize(cx.tcx, cx.param_env)
|
||||
.map_or(true, |u| u != 0)
|
||||
&& self.is_interior_mutable_type(cx, inner_ty)
|
||||
},
|
||||
Tuple(fields) => fields.iter().any(|ty| self.is_interior_mutable_type(cx, ty)),
|
||||
Adt(def, substs) => {
|
||||
// Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
|
||||
// that of their type parameters. Note: we don't include `HashSet` and `HashMap`
|
||||
// because they have no impl for `Hash` or `Ord`.
|
||||
let def_id = def.did();
|
||||
let is_std_collection = [
|
||||
sym::Option,
|
||||
sym::Result,
|
||||
sym::LinkedList,
|
||||
sym::Vec,
|
||||
sym::VecDeque,
|
||||
sym::BTreeMap,
|
||||
sym::BTreeSet,
|
||||
sym::Rc,
|
||||
sym::Arc,
|
||||
]
|
||||
.iter()
|
||||
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id));
|
||||
let is_box = Some(def_id) == cx.tcx.lang_items().owned_box();
|
||||
if is_std_collection || is_box || self.ignore_mut_def_ids.contains(&def_id) {
|
||||
// The type is mutable if any of its type parameters are
|
||||
substs.types().any(|ty| self.is_interior_mutable_type(cx, ty))
|
||||
} else {
|
||||
!ty.has_escaping_bound_vars()
|
||||
&& cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
|
||||
&& !ty.is_freeze(cx.tcx, cx.param_env)
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -340,18 +340,11 @@ fn suggest_bool_comparison<'a, 'tcx>(
|
|||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
expr: &Expr<'_>,
|
||||
mut applicability: Applicability,
|
||||
mut app: Applicability,
|
||||
message: &str,
|
||||
conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
|
||||
) {
|
||||
let hint = if expr.span.from_expansion() {
|
||||
if applicability != Applicability::Unspecified {
|
||||
applicability = Applicability::MaybeIncorrect;
|
||||
}
|
||||
Sugg::hir_with_macro_callsite(cx, expr, "..")
|
||||
} else {
|
||||
Sugg::hir_with_applicability(cx, expr, "..", &mut applicability)
|
||||
};
|
||||
let hint = Sugg::hir_with_context(cx, expr, e.span.ctxt(), "..", &mut app);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BOOL_COMPARISON,
|
||||
|
@ -359,7 +352,7 @@ fn suggest_bool_comparison<'a, 'tcx>(
|
|||
message,
|
||||
"try simplifying it as shown",
|
||||
conv_hint(hint).to_string(),
|
||||
applicability,
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{BytePos, Pos};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -38,25 +39,28 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
|
|||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if let ItemKind::Fn(fn_sig, _, _) = &item.kind {
|
||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut applicability);
|
||||
let mut app = Applicability::MaybeIncorrect;
|
||||
let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut app);
|
||||
for attr in attrs {
|
||||
if let Some(ident) = attr.ident()
|
||||
&& ident.name == rustc_span::sym::no_mangle
|
||||
&& fn_sig.header.abi == Abi::Rust
|
||||
&& !snippet.contains("extern") {
|
||||
&& let Some((fn_attrs, _)) = snippet.split_once("fn")
|
||||
&& !fn_attrs.contains("extern")
|
||||
{
|
||||
let sugg_span = fn_sig.span
|
||||
.with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len()))
|
||||
.shrink_to_lo();
|
||||
|
||||
let suggestion = snippet.split_once("fn")
|
||||
.map_or(String::new(), |(first, second)| format!(r#"{first}extern "C" fn{second}"#));
|
||||
|
||||
span_lint_and_sugg(
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NO_MANGLE_WITH_RUST_ABI,
|
||||
fn_sig.span,
|
||||
"attribute #[no_mangle] set on a Rust ABI function",
|
||||
"try",
|
||||
suggestion,
|
||||
applicability
|
||||
"`#[no_mangle]` set on a function with the default (`Rust`) ABI",
|
||||
|diag| {
|
||||
diag.span_suggestion(sugg_span, "set an ABI", "extern \"C\" ", app)
|
||||
.span_suggestion(sugg_span, "or explicitly set the default", "extern \"Rust\" ", app);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ use rustc_hir::{
|
|||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::SyntaxContext;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -95,10 +96,10 @@ struct OptionOccurrence {
|
|||
none_expr: String,
|
||||
}
|
||||
|
||||
fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
|
||||
fn format_option_in_sugg(cond_sugg: Sugg<'_>, as_ref: bool, as_mut: bool) -> String {
|
||||
format!(
|
||||
"{}{}",
|
||||
Sugg::hir_with_macro_callsite(cx, cond_expr, "..").maybe_par(),
|
||||
cond_sugg.maybe_par(),
|
||||
if as_mut {
|
||||
".as_mut()"
|
||||
} else if as_ref {
|
||||
|
@ -111,6 +112,7 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
|
|||
|
||||
fn try_get_option_occurrence<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
pat: &Pat<'tcx>,
|
||||
expr: &Expr<'_>,
|
||||
if_then: &'tcx Expr<'_>,
|
||||
|
@ -160,11 +162,23 @@ fn try_get_option_occurrence<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
let mut app = Applicability::Unspecified;
|
||||
return Some(OptionOccurrence {
|
||||
option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
|
||||
option: format_option_in_sugg(
|
||||
Sugg::hir_with_context(cx, cond_expr, ctxt, "..", &mut app),
|
||||
as_ref,
|
||||
as_mut,
|
||||
),
|
||||
method_sugg: method_sugg.to_string(),
|
||||
some_expr: format!("|{capture_mut}{capture_name}| {}", Sugg::hir_with_macro_callsite(cx, some_body, "..")),
|
||||
none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")),
|
||||
some_expr: format!(
|
||||
"|{capture_mut}{capture_name}| {}",
|
||||
Sugg::hir_with_context(cx, some_body, ctxt, "..", &mut app),
|
||||
),
|
||||
none_expr: format!(
|
||||
"{}{}",
|
||||
if method_sugg == "map_or" { "" } else { "|| " },
|
||||
Sugg::hir_with_context(cx, none_body, ctxt, "..", &mut app),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +208,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
|
|||
}) = higher::IfLet::hir(cx, expr)
|
||||
{
|
||||
if !is_else_clause(cx.tcx, expr) {
|
||||
return try_get_option_occurrence(cx, let_pat, let_expr, if_then, if_else);
|
||||
return try_get_option_occurrence(cx, expr.span.ctxt(), let_pat, let_expr, if_then, if_else);
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -203,7 +217,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
|
|||
fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurrence> {
|
||||
if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
|
||||
if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) {
|
||||
return try_get_option_occurrence(cx, let_pat, ex, if_then, if_else);
|
||||
return try_get_option_occurrence(cx, expr.span.ctxt(), let_pat, ex, if_then, if_else);
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
|
||||
use rustc_ast::ast::*;
|
||||
use rustc_ast::ast::{Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_ast::visit::Visitor as AstVisitor;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
|
@ -32,7 +32,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.69.0"]
|
||||
pub REDUNDANT_ASYNC_BLOCK,
|
||||
complexity,
|
||||
nursery,
|
||||
"`async { future.await }` can be replaced by `future`"
|
||||
}
|
||||
declare_lint_pass!(RedundantAsyncBlock => [REDUNDANT_ASYNC_BLOCK]);
|
||||
|
@ -48,6 +48,11 @@ impl EarlyLintPass for RedundantAsyncBlock {
|
|||
!future.span.from_expansion() &&
|
||||
!await_in_expr(future)
|
||||
{
|
||||
if captures_value(last) {
|
||||
// If the async block captures variables then there is no equivalence.
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REDUNDANT_ASYNC_BLOCK,
|
||||
|
@ -82,3 +87,33 @@ impl<'ast> AstVisitor<'ast> for AwaitDetector {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether an expression may have captured a local variable.
|
||||
/// This is done by looking for paths with only one segment, except as
|
||||
/// a prefix of `.await` since this would be captured by value.
|
||||
///
|
||||
/// This function will sometimes return `true` even tough there are no
|
||||
/// captures happening: at the AST level, it is impossible to
|
||||
/// dinstinguish a function call from a call to a closure which comes
|
||||
/// from the local environment.
|
||||
fn captures_value(expr: &Expr) -> bool {
|
||||
let mut detector = CaptureDetector::default();
|
||||
detector.visit_expr(expr);
|
||||
detector.capture_found
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CaptureDetector {
|
||||
capture_found: bool,
|
||||
}
|
||||
|
||||
impl<'ast> AstVisitor<'ast> for CaptureDetector {
|
||||
fn visit_expr(&mut self, ex: &'ast Expr) {
|
||||
match (&ex.kind, self.capture_found) {
|
||||
(ExprKind::Await(fut), _) if matches!(fut.kind, ExprKind::Path(..)) => (),
|
||||
(ExprKind::Path(_, path), _) if path.segments.len() == 1 => self.capture_found = true,
|
||||
(_, false) => rustc_ast::visit::walk_expr(self, ex),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::rustc_lint::LintContext;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::sugg;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, ExprKind};
|
||||
|
@ -44,7 +43,8 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned {
|
|||
if let Some(expr) = block.expr;
|
||||
let t_expr = cx.typeck_results().expr_ty(expr);
|
||||
if t_expr.is_unit();
|
||||
if let snippet = snippet_with_macro_callsite(cx, expr.span, "}");
|
||||
let mut app = Applicability::MaybeIncorrect;
|
||||
if let snippet = snippet_with_context(cx, expr.span, block.span.ctxt(), "}", &mut app).0;
|
||||
if !snippet.ends_with('}') && !snippet.ends_with(';');
|
||||
if cx.sess().source_map().is_multiline(block.span);
|
||||
then {
|
||||
|
@ -52,17 +52,14 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned {
|
|||
if let ExprKind::DropTemps(..) = &expr.kind {
|
||||
return;
|
||||
}
|
||||
|
||||
let sugg = sugg::Sugg::hir_with_macro_callsite(cx, expr, "..");
|
||||
let suggestion = format!("{sugg};");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SEMICOLON_IF_NOTHING_RETURNED,
|
||||
expr.span.source_callsite(),
|
||||
"consider adding a `;` to the last statement for consistent formatting",
|
||||
"add a `;` here",
|
||||
suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
format!("{snippet};"),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::FxHashSet;
|
||||
use clippy_utils::{
|
||||
diagnostics::span_lint_and_then,
|
||||
get_attr,
|
||||
source::{indent_of, snippet},
|
||||
};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use rustc_hir::{
|
||||
self as hir,
|
||||
|
@ -58,6 +58,7 @@ impl_lint_pass!(SignificantDropTightening<'_> => [SIGNIFICANT_DROP_TIGHTENING]);
|
|||
pub struct SignificantDropTightening<'tcx> {
|
||||
/// Auxiliary structure used to avoid having to verify the same type multiple times.
|
||||
seen_types: FxHashSet<Ty<'tcx>>,
|
||||
type_cache: FxHashMap<Ty<'tcx>, bool>,
|
||||
}
|
||||
|
||||
impl<'tcx> SignificantDropTightening<'tcx> {
|
||||
|
@ -118,7 +119,7 @@ impl<'tcx> SignificantDropTightening<'tcx> {
|
|||
stmt: &hir::Stmt<'_>,
|
||||
cb: impl Fn(&mut SigDropAuxParams),
|
||||
) {
|
||||
let mut sig_drop_finder = SigDropFinder::new(cx, &mut self.seen_types);
|
||||
let mut sig_drop_finder = SigDropFinder::new(cx, &mut self.seen_types, &mut self.type_cache);
|
||||
sig_drop_finder.visit_expr(expr);
|
||||
if sig_drop_finder.has_sig_drop {
|
||||
cb(sdap);
|
||||
|
@ -296,15 +297,24 @@ impl Default for SigDropAuxParams {
|
|||
struct SigDropChecker<'cx, 'sdt, 'tcx> {
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
seen_types: &'sdt mut FxHashSet<Ty<'tcx>>,
|
||||
type_cache: &'sdt mut FxHashMap<Ty<'tcx>, bool>,
|
||||
}
|
||||
|
||||
impl<'cx, 'sdt, 'tcx> SigDropChecker<'cx, 'sdt, 'tcx> {
|
||||
pub(crate) fn new(cx: &'cx LateContext<'tcx>, seen_types: &'sdt mut FxHashSet<Ty<'tcx>>) -> Self {
|
||||
pub(crate) fn new(
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
seen_types: &'sdt mut FxHashSet<Ty<'tcx>>,
|
||||
type_cache: &'sdt mut FxHashMap<Ty<'tcx>, bool>,
|
||||
) -> Self {
|
||||
seen_types.clear();
|
||||
Self { cx, seen_types }
|
||||
Self {
|
||||
cx,
|
||||
seen_types,
|
||||
type_cache,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
|
||||
pub(crate) fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>) -> bool {
|
||||
if let Some(adt) = ty.ty_adt_def() {
|
||||
let mut iter = get_attr(
|
||||
self.cx.sess(),
|
||||
|
@ -340,6 +350,16 @@ impl<'cx, 'sdt, 'tcx> SigDropChecker<'cx, 'sdt, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
|
||||
// The borrow checker prevents us from using something fancier like or_insert_with.
|
||||
if let Some(ty) = self.type_cache.get(&ty) {
|
||||
return *ty;
|
||||
}
|
||||
let value = self.has_sig_drop_attr_uncached(ty);
|
||||
self.type_cache.insert(ty, value);
|
||||
value
|
||||
}
|
||||
|
||||
fn has_seen_ty(&mut self, ty: Ty<'tcx>) -> bool {
|
||||
!self.seen_types.insert(ty)
|
||||
}
|
||||
|
@ -353,11 +373,15 @@ struct SigDropFinder<'cx, 'sdt, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'cx, 'sdt, 'tcx> SigDropFinder<'cx, 'sdt, 'tcx> {
|
||||
fn new(cx: &'cx LateContext<'tcx>, seen_types: &'sdt mut FxHashSet<Ty<'tcx>>) -> Self {
|
||||
fn new(
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
seen_types: &'sdt mut FxHashSet<Ty<'tcx>>,
|
||||
type_cache: &'sdt mut FxHashMap<Ty<'tcx>, bool>,
|
||||
) -> Self {
|
||||
Self {
|
||||
cx,
|
||||
has_sig_drop: false,
|
||||
sig_drop_checker: SigDropChecker::new(cx, seen_types),
|
||||
sig_drop_checker: SigDropChecker::new(cx, seen_types, type_cache),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core}
|
|||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
@ -188,8 +189,10 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
|
|||
if let Some((lhs0, rhs0)) = parse(first)
|
||||
&& let Some((lhs1, rhs1)) = parse(second)
|
||||
&& first.span.eq_ctxt(second.span)
|
||||
&& !in_external_macro(cx.sess(), first.span)
|
||||
&& is_same(cx, lhs0, rhs1)
|
||||
&& is_same(cx, lhs1, rhs0)
|
||||
&& !is_same(cx, lhs1, rhs1) // Ignore a = b; a = a (#10421)
|
||||
&& let Some(lhs_sugg) = match &lhs0 {
|
||||
ExprOrIdent::Expr(expr) => Sugg::hir_opt(cx, expr),
|
||||
ExprOrIdent::Ident(ident) => Some(Sugg::NonParen(ident.as_str().into())),
|
||||
|
@ -257,8 +260,8 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr<
|
|||
/// Implementation of the xor case for `MANUAL_SWAP` lint.
|
||||
fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
|
||||
for [s1, s2, s3] in block.stmts.array_windows::<3>() {
|
||||
let ctxt = s1.span.ctxt();
|
||||
if_chain! {
|
||||
let ctxt = s1.span.ctxt();
|
||||
if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt);
|
||||
if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt);
|
||||
if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::get_parent_node;
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -52,12 +52,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
|
|||
"this let-binding has unit value",
|
||||
|diag| {
|
||||
if let Some(expr) = &local.init {
|
||||
let snip = snippet_with_macro_callsite(cx, expr.span, "()");
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let snip = snippet_with_context(cx, expr.span, local.span.ctxt(), "()", &mut app).0;
|
||||
diag.span_suggestion(
|
||||
local.span,
|
||||
"omit the `let` binding",
|
||||
format!("{snip};"),
|
||||
Applicability::MachineApplicable, // snippet
|
||||
app,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
84
clippy_lints/src/unnecessary_struct_initialization.rs
Normal file
84
clippy_lints/src/unnecessary_struct_initialization.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
use clippy_utils::{diagnostics::span_lint_and_sugg, get_parent_expr, path_to_local, source::snippet, ty::is_copy};
|
||||
use rustc_hir::{BindingAnnotation, Expr, ExprKind, Node, PatKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for initialization of a `struct` by copying a base without setting
|
||||
/// any field.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readibility suffers from unnecessary struct building.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// struct S { s: String }
|
||||
///
|
||||
/// let a = S { s: String::from("Hello, world!") };
|
||||
/// let b = S { ..a };
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct S { s: String }
|
||||
///
|
||||
/// let a = S { s: String::from("Hello, world!") };
|
||||
/// let b = a;
|
||||
/// ```
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub UNNECESSARY_STRUCT_INITIALIZATION,
|
||||
complexity,
|
||||
"struct built from a base that can be written mode concisely"
|
||||
}
|
||||
declare_lint_pass!(UnnecessaryStruct => [UNNECESSARY_STRUCT_INITIALIZATION]);
|
||||
|
||||
impl LateLintPass<'_> for UnnecessaryStruct {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Struct(_, &[], Some(base)) = expr.kind {
|
||||
if let Some(parent) = get_parent_expr(cx, expr) &&
|
||||
let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) &&
|
||||
parent_ty.is_any_ptr()
|
||||
{
|
||||
if is_copy(cx, cx.typeck_results().expr_ty(expr)) && path_to_local(base).is_some() {
|
||||
// When the type implements `Copy`, a reference to the new struct works on the
|
||||
// copy. Using the original would borrow it.
|
||||
return;
|
||||
}
|
||||
|
||||
if parent_ty.is_mutable_ptr() && !is_mutable(cx, base) {
|
||||
// The original can be used in a mutable reference context only if it is mutable.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: do not propose to replace *XX if XX is not Copy
|
||||
if let ExprKind::Unary(UnOp::Deref, target) = base.kind &&
|
||||
matches!(target.kind, ExprKind::Path(..)) &&
|
||||
!is_copy(cx, cx.typeck_results().expr_ty(expr))
|
||||
{
|
||||
// `*base` cannot be used instead of the struct in the general case if it is not Copy.
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_STRUCT_INITIALIZATION,
|
||||
expr.span,
|
||||
"unnecessary struct building",
|
||||
"replace with",
|
||||
snippet(cx, base.span, "..").into_owned(),
|
||||
rustc_errors::Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let Some(hir_id) = path_to_local(expr) &&
|
||||
let Node::Pat(pat) = cx.tcx.hir().get(hir_id)
|
||||
{
|
||||
matches!(pat.kind, PatKind::Binding(BindingAnnotation::MUT, ..))
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
|
||||
use clippy_utils::source::{snippet, snippet_with_macro_callsite};
|
||||
use clippy_utils::source::{snippet, snippet_with_context};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts};
|
||||
use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths};
|
||||
|
@ -68,15 +68,16 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||
let a = cx.typeck_results().expr_ty(e);
|
||||
let b = cx.typeck_results().expr_ty(recv);
|
||||
if same_type_and_consts(a, b) {
|
||||
let sugg = snippet_with_macro_callsite(cx, recv.span, "<expr>").to_string();
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let sugg = snippet_with_context(cx, recv.span, e.span.ctxt(), "<expr>", &mut app).0;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
USELESS_CONVERSION,
|
||||
e.span,
|
||||
&format!("useless conversion to the same type: `{b}`"),
|
||||
"consider removing `.into()`",
|
||||
sugg,
|
||||
Applicability::MachineApplicable, // snippet
|
||||
sugg.into_owned(),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||
if same_type_and_consts(a, b);
|
||||
|
||||
then {
|
||||
let sugg = Sugg::hir_with_macro_callsite(cx, arg, "<expr>").maybe_par();
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "<expr>", &mut app).maybe_par();
|
||||
let sugg_msg =
|
||||
format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
|
||||
span_lint_and_sugg(
|
||||
|
@ -175,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||
&format!("useless conversion to the same type: `{b}`"),
|
||||
&sugg_msg,
|
||||
sugg.to_string(),
|
||||
Applicability::MachineApplicable, // snippet
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -437,7 +437,7 @@ define_Conf! {
|
|||
///
|
||||
/// The maximum size of the `Err`-variant in a `Result` returned from a function
|
||||
(large_error_threshold: u64 = 128),
|
||||
/// Lint: MUTABLE_KEY_TYPE.
|
||||
/// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND.
|
||||
///
|
||||
/// A list of paths to types that should be treated like `Arc`, i.e. ignored but
|
||||
/// for the generic parameters for determining interior mutability
|
||||
|
|
|
@ -158,12 +158,10 @@ impl LateLintPass<'_> for WildcardImports {
|
|||
let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(false);
|
||||
let imports_string = if imports.len() == 1 {
|
||||
imports.pop().unwrap()
|
||||
} else if braced_glob {
|
||||
imports.join(", ")
|
||||
} else {
|
||||
if braced_glob {
|
||||
imports.join(", ")
|
||||
} else {
|
||||
format!("{{{}}}", imports.join(", "))
|
||||
}
|
||||
format!("{{{}}}", imports.join(", "))
|
||||
};
|
||||
|
||||
let sugg = if braced_glob {
|
||||
|
|
|
@ -146,7 +146,7 @@ pub fn get_unique_attr<'a>(
|
|||
/// Return true if the attributes contain any of `proc_macro`,
|
||||
/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
|
||||
pub fn is_proc_macro(attrs: &[ast::Attribute]) -> bool {
|
||||
attrs.iter().any(|attr| attr.is_proc_macro_attr())
|
||||
attrs.iter().any(rustc_ast::Attribute::is_proc_macro_attr)
|
||||
}
|
||||
|
||||
/// Return true if the attributes contain `#[doc(hidden)]`
|
||||
|
|
|
@ -199,10 +199,9 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
|||
},
|
||||
|
||||
// Memory allocation, custom operator, loop, or call to an unknown function
|
||||
ExprKind::Unary(..)
|
||||
| ExprKind::Binary(..)
|
||||
| ExprKind::Loop(..)
|
||||
| ExprKind::Call(..) => self.eagerness = Lazy,
|
||||
ExprKind::Unary(..) | ExprKind::Binary(..) | ExprKind::Loop(..) | ExprKind::Call(..) => {
|
||||
self.eagerness = Lazy;
|
||||
},
|
||||
|
||||
ExprKind::ConstBlock(_)
|
||||
| ExprKind::Array(_)
|
||||
|
|
|
@ -19,6 +19,7 @@ macro_rules! msrv_aliases {
|
|||
|
||||
// names may refer to stabilized feature flags or library items
|
||||
msrv_aliases! {
|
||||
1,68,0 { PATH_MAIN_SEPARATOR_STR }
|
||||
1,65,0 { LET_ELSE }
|
||||
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
|
||||
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }
|
||||
|
|
|
@ -67,6 +67,7 @@ pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard
|
|||
pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
|
||||
pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
|
||||
pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
|
||||
pub const PATH_MAIN_SEPARATOR: [&str; 3] = ["std", "path", "MAIN_SEPARATOR"];
|
||||
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
|
||||
pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekable"];
|
||||
pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
|
||||
|
|
|
@ -176,9 +176,10 @@ fn check_rvalue<'tcx>(
|
|||
// FIXME(dyn-star)
|
||||
unimplemented!()
|
||||
},
|
||||
Rvalue::Cast(CastKind::Transmute, _, _) => {
|
||||
Err((span, "transmute can attempt to turn pointers into integers, so is unstable in const fn".into()))
|
||||
},
|
||||
Rvalue::Cast(CastKind::Transmute, _, _) => Err((
|
||||
span,
|
||||
"transmute can attempt to turn pointers into integers, so is unstable in const fn".into(),
|
||||
)),
|
||||
// binops are fine on integers
|
||||
Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
|
||||
check_operand(tcx, lhs, span, body)?;
|
||||
|
|
|
@ -12,24 +12,21 @@ use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP};
|
|||
use std::borrow::Cow;
|
||||
|
||||
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
|
||||
/// Also takes an `Option<String>` which can be put inside the braces.
|
||||
pub fn expr_block<'a, T: LintContext>(
|
||||
pub fn expr_block<T: LintContext>(
|
||||
cx: &T,
|
||||
expr: &Expr<'_>,
|
||||
option: Option<String>,
|
||||
default: &'a str,
|
||||
outer: SyntaxContext,
|
||||
default: &str,
|
||||
indent_relative_to: Option<Span>,
|
||||
) -> Cow<'a, str> {
|
||||
let code = snippet_block(cx, expr.span, default, indent_relative_to);
|
||||
let string = option.unwrap_or_default();
|
||||
if expr.span.from_expansion() {
|
||||
Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
|
||||
app: &mut Applicability,
|
||||
) -> String {
|
||||
let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app);
|
||||
if from_macro {
|
||||
format!("{{ {code} }}")
|
||||
} else if let ExprKind::Block(_, _) = expr.kind {
|
||||
Cow::Owned(format!("{code}{string}"))
|
||||
} else if string.is_empty() {
|
||||
Cow::Owned(format!("{{ {code} }}"))
|
||||
format!("{code}")
|
||||
} else {
|
||||
Cow::Owned(format!("{{\n{code};\n{string}\n}}"))
|
||||
format!("{{ {code} }}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,12 +226,6 @@ fn snippet_with_applicability_sess<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
/// Same as `snippet`, but should only be used when it's clear that the input span is
|
||||
/// not a macro argument.
|
||||
pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
|
||||
snippet(cx, span.source_callsite(), default)
|
||||
}
|
||||
|
||||
/// Converts a span to a code snippet. Returns `None` if not available.
|
||||
pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> {
|
||||
snippet_opt_sess(cx.sess(), span)
|
||||
|
@ -303,6 +294,19 @@ pub fn snippet_block_with_applicability<'a>(
|
|||
reindent_multiline(snip, true, indent)
|
||||
}
|
||||
|
||||
pub fn snippet_block_with_context<'a>(
|
||||
cx: &impl LintContext,
|
||||
span: Span,
|
||||
outer: SyntaxContext,
|
||||
default: &'a str,
|
||||
indent_relative_to: Option<Span>,
|
||||
app: &mut Applicability,
|
||||
) -> (Cow<'a, str>, bool) {
|
||||
let (snip, from_macro) = snippet_with_context(cx, span, outer, default, app);
|
||||
let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
|
||||
(reindent_multiline(snip, true, indent), from_macro)
|
||||
}
|
||||
|
||||
/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
|
||||
/// will result in the macro call, rather then the expansion, if the span is from a child context.
|
||||
/// If the span is not from a child context, it will be used directly instead.
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
//! Contains utility functions to generate suggestions.
|
||||
#![deny(clippy::missing_docs_in_private_items)]
|
||||
|
||||
use crate::source::{
|
||||
snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite,
|
||||
};
|
||||
use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_context};
|
||||
use crate::ty::expr_sig;
|
||||
use crate::{get_parent_expr_for_hir, higher};
|
||||
use rustc_ast::util::parser::AssocOp;
|
||||
|
@ -89,12 +87,6 @@ impl<'a> Sugg<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro.
|
||||
pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
|
||||
let get_snippet = |span| snippet_with_macro_callsite(cx, span, default);
|
||||
Self::hir_from_snippet(expr, get_snippet)
|
||||
}
|
||||
|
||||
/// Same as `hir`, but first walks the span up to the given context. This will result in the
|
||||
/// macro call, rather then the expansion, if the span is from a child context. If the span is
|
||||
/// not from a child context, it will be used directly instead.
|
||||
|
|
|
@ -16,9 +16,9 @@ use rustc_infer::infer::{
|
|||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::interpret::{ConstValue, Scalar};
|
||||
use rustc_middle::ty::{
|
||||
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind,
|
||||
Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||
UintTy, VariantDef, VariantDiscr,
|
||||
self, layout::ValidityRequirement, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv,
|
||||
Predicate, PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
|
||||
TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
|
||||
};
|
||||
use rustc_middle::ty::{GenericArg, GenericArgKind};
|
||||
use rustc_span::symbol::Ident;
|
||||
|
@ -538,13 +538,12 @@ pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
|
|||
}
|
||||
|
||||
/// Checks if a given type looks safe to be uninitialized.
|
||||
pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||
match *ty.kind() {
|
||||
ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component),
|
||||
ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
|
||||
ty::Adt(adt, _) => cx.tcx.lang_items().maybe_uninit() == Some(adt.did()),
|
||||
_ => false,
|
||||
}
|
||||
pub fn is_uninit_value_valid_for_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
cx.tcx
|
||||
.check_validity_requirement((ValidityRequirement::Uninit, cx.param_env.and(ty)))
|
||||
// For types containing generic parameters we cannot get a layout to check.
|
||||
// Therefore, we are conservative and assume that they don't allow uninit.
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Gets an iterator over all predicates which apply to the given item.
|
||||
|
@ -1121,3 +1120,47 @@ pub fn make_normalized_projection<'tcx>(
|
|||
}
|
||||
helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?)
|
||||
}
|
||||
|
||||
/// Check if given type has inner mutability such as [`std::cell::Cell`] or [`std::cell::RefCell`]
|
||||
/// etc.
|
||||
pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
match *ty.kind() {
|
||||
ty::Ref(_, inner_ty, mutbl) => mutbl == Mutability::Mut || is_interior_mut_ty(cx, inner_ty),
|
||||
ty::Slice(inner_ty) => is_interior_mut_ty(cx, inner_ty),
|
||||
ty::Array(inner_ty, size) => {
|
||||
size.try_eval_target_usize(cx.tcx, cx.param_env)
|
||||
.map_or(true, |u| u != 0)
|
||||
&& is_interior_mut_ty(cx, inner_ty)
|
||||
},
|
||||
ty::Tuple(fields) => fields.iter().any(|ty| is_interior_mut_ty(cx, ty)),
|
||||
ty::Adt(def, substs) => {
|
||||
// Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
|
||||
// that of their type parameters. Note: we don't include `HashSet` and `HashMap`
|
||||
// because they have no impl for `Hash` or `Ord`.
|
||||
let def_id = def.did();
|
||||
let is_std_collection = [
|
||||
sym::Option,
|
||||
sym::Result,
|
||||
sym::LinkedList,
|
||||
sym::Vec,
|
||||
sym::VecDeque,
|
||||
sym::BTreeMap,
|
||||
sym::BTreeSet,
|
||||
sym::Rc,
|
||||
sym::Arc,
|
||||
]
|
||||
.iter()
|
||||
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id));
|
||||
let is_box = Some(def_id) == cx.tcx.lang_items().owned_box();
|
||||
if is_std_collection || is_box {
|
||||
// The type is mutable if any of its type parameters are
|
||||
substs.types().any(|ty| is_interior_mut_ty(cx, ty))
|
||||
} else {
|
||||
!ty.has_escaping_bound_vars()
|
||||
&& cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
|
||||
&& !ty.is_freeze(cx.tcx, cx.param_env)
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -599,9 +599,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
|
|||
| ExprKind::Let(&Let { init: e, .. }) => {
|
||||
helper(typeck, false, e, f)?;
|
||||
},
|
||||
ExprKind::Block(&Block { expr: Some(e), .. }, _)
|
||||
| ExprKind::Cast(e, _)
|
||||
| ExprKind::Unary(_, e) => {
|
||||
ExprKind::Block(&Block { expr: Some(e), .. }, _) | ExprKind::Cast(e, _) | ExprKind::Unary(_, e) => {
|
||||
helper(typeck, true, e, f)?;
|
||||
},
|
||||
ExprKind::Call(callee, args) => {
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "nightly-2023-03-10"
|
||||
channel = "nightly-2023-03-24"
|
||||
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||
|
|
|
@ -37,10 +37,10 @@ fn dogfood_clippy() {
|
|||
}
|
||||
|
||||
assert!(
|
||||
!failed_packages.is_empty(),
|
||||
failed_packages.is_empty(),
|
||||
"Dogfood failed for packages `{}`",
|
||||
failed_packages.iter().format(", "),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -9,3 +9,4 @@ note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy
|
|||
|
||||
note: Clippy version: foo
|
||||
|
||||
thread panicked while panicking. aborting.
|
||||
|
|
1
tests/ui-toml/ifs_same_cond/clippy.toml
Normal file
1
tests/ui-toml/ifs_same_cond/clippy.toml
Normal file
|
@ -0,0 +1 @@
|
|||
ignore-interior-mutability = ["std::cell::Cell"]
|
18
tests/ui-toml/ifs_same_cond/ifs_same_cond.rs
Normal file
18
tests/ui-toml/ifs_same_cond/ifs_same_cond.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
#![warn(clippy::ifs_same_cond)]
|
||||
#![allow(clippy::if_same_then_else, clippy::comparison_chain)]
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn issue10272() {
|
||||
use std::cell::Cell;
|
||||
|
||||
// Because the `ignore-interior-mutability` configuration
|
||||
// is set to ignore for `std::cell::Cell`, the following `get()` calls
|
||||
// should trigger warning
|
||||
let x = Cell::new(true);
|
||||
if x.get() {
|
||||
} else if !x.take() {
|
||||
} else if x.get() {
|
||||
} else {
|
||||
}
|
||||
}
|
15
tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr
Normal file
15
tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr
Normal file
|
@ -0,0 +1,15 @@
|
|||
error: this `if` has the same condition as a previous `if`
|
||||
--> $DIR/ifs_same_cond.rs:15:15
|
||||
|
|
||||
LL | } else if x.get() {
|
||||
| ^^^^^^^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/ifs_same_cond.rs:13:8
|
||||
|
|
||||
LL | if x.get() {
|
||||
| ^^^^^^^
|
||||
= note: `-D clippy::ifs-same-cond` implied by `-D warnings`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
25
tests/ui/allow_attributes.fixed
Normal file
25
tests/ui/allow_attributes.fixed
Normal file
|
@ -0,0 +1,25 @@
|
|||
// run-rustfix
|
||||
#![allow(unused)]
|
||||
#![warn(clippy::allow_attributes)]
|
||||
#![feature(lint_reasons)]
|
||||
|
||||
fn main() {}
|
||||
|
||||
// Using clippy::needless_borrow just as a placeholder, it isn't relevant.
|
||||
|
||||
// Should lint
|
||||
#[expect(dead_code)]
|
||||
struct T1;
|
||||
|
||||
struct T2; // Should not lint
|
||||
#[deny(clippy::needless_borrow)] // Should not lint
|
||||
struct T3;
|
||||
#[warn(clippy::needless_borrow)] // Should not lint
|
||||
struct T4;
|
||||
// `panic = "unwind"` should always be true
|
||||
#[cfg_attr(panic = "unwind", expect(dead_code))]
|
||||
struct CfgT;
|
||||
|
||||
fn ignore_inner_attr() {
|
||||
#![allow(unused)] // Should not lint
|
||||
}
|
25
tests/ui/allow_attributes.rs
Normal file
25
tests/ui/allow_attributes.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
// run-rustfix
|
||||
#![allow(unused)]
|
||||
#![warn(clippy::allow_attributes)]
|
||||
#![feature(lint_reasons)]
|
||||
|
||||
fn main() {}
|
||||
|
||||
// Using clippy::needless_borrow just as a placeholder, it isn't relevant.
|
||||
|
||||
// Should lint
|
||||
#[allow(dead_code)]
|
||||
struct T1;
|
||||
|
||||
struct T2; // Should not lint
|
||||
#[deny(clippy::needless_borrow)] // Should not lint
|
||||
struct T3;
|
||||
#[warn(clippy::needless_borrow)] // Should not lint
|
||||
struct T4;
|
||||
// `panic = "unwind"` should always be true
|
||||
#[cfg_attr(panic = "unwind", allow(dead_code))]
|
||||
struct CfgT;
|
||||
|
||||
fn ignore_inner_attr() {
|
||||
#![allow(unused)] // Should not lint
|
||||
}
|
16
tests/ui/allow_attributes.stderr
Normal file
16
tests/ui/allow_attributes.stderr
Normal file
|
@ -0,0 +1,16 @@
|
|||
error: #[allow] attribute found
|
||||
--> $DIR/allow_attributes.rs:11:3
|
||||
|
|
||||
LL | #[allow(dead_code)]
|
||||
| ^^^^^ help: replace it with: `expect`
|
||||
|
|
||||
= note: `-D clippy::allow-attributes` implied by `-D warnings`
|
||||
|
||||
error: #[allow] attribute found
|
||||
--> $DIR/allow_attributes.rs:20:30
|
||||
|
|
||||
LL | #[cfg_attr(panic = "unwind", allow(dead_code))]
|
||||
| ^^^^^ help: replace it with: `expect`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
// run-rustfix
|
||||
// edition:2018
|
||||
// aux-build:macro_rules.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![feature(exclusive_range_pattern)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
|
@ -9,33 +9,10 @@
|
|||
#![allow(clippy::needless_parens_on_range_literals)]
|
||||
#![allow(clippy::double_parens)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
|
||||
macro_rules! a {
|
||||
() => {
|
||||
'a'
|
||||
};
|
||||
}
|
||||
macro_rules! A {
|
||||
() => {
|
||||
'A'
|
||||
};
|
||||
}
|
||||
macro_rules! zero {
|
||||
() => {
|
||||
'0'
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! b {
|
||||
() => {
|
||||
let _ = 'a'..='z';
|
||||
let _ = 'A'..='Z';
|
||||
let _ = '0'..='9';
|
||||
};
|
||||
}
|
||||
extern crate proc_macros;
|
||||
use proc_macros::{external, inline_macros};
|
||||
|
||||
#[inline_macros]
|
||||
fn main() {
|
||||
#[rustfmt::skip]
|
||||
{
|
||||
|
@ -56,9 +33,9 @@ fn main() {
|
|||
let _ = b'B'..b'Z';
|
||||
let _ = b'1'..b'9';
|
||||
|
||||
let _ = a!()..='z';
|
||||
let _ = A!()..='Z';
|
||||
let _ = zero!()..='9';
|
||||
let _ = inline!('a')..='z';
|
||||
let _ = inline!('A')..='Z';
|
||||
let _ = inline!('0')..='9';
|
||||
|
||||
let _ = match 0u8 {
|
||||
b'a'..=b'z' if true => 1,
|
||||
|
@ -80,8 +57,16 @@ fn main() {
|
|||
_ => 7,
|
||||
};
|
||||
|
||||
almost_complete_range!();
|
||||
b!();
|
||||
external!(
|
||||
let _ = 'a'..'z';
|
||||
let _ = 'A'..'Z';
|
||||
let _ = '0'..'9';
|
||||
);
|
||||
inline!(
|
||||
let _ = 'a'..='z';
|
||||
let _ = 'A'..='Z';
|
||||
let _ = '0'..='9';
|
||||
);
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.25"]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// run-rustfix
|
||||
// edition:2018
|
||||
// aux-build:macro_rules.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![feature(exclusive_range_pattern)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
|
@ -9,33 +9,10 @@
|
|||
#![allow(clippy::needless_parens_on_range_literals)]
|
||||
#![allow(clippy::double_parens)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
|
||||
macro_rules! a {
|
||||
() => {
|
||||
'a'
|
||||
};
|
||||
}
|
||||
macro_rules! A {
|
||||
() => {
|
||||
'A'
|
||||
};
|
||||
}
|
||||
macro_rules! zero {
|
||||
() => {
|
||||
'0'
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! b {
|
||||
() => {
|
||||
let _ = 'a'..'z';
|
||||
let _ = 'A'..'Z';
|
||||
let _ = '0'..'9';
|
||||
};
|
||||
}
|
||||
extern crate proc_macros;
|
||||
use proc_macros::{external, inline_macros};
|
||||
|
||||
#[inline_macros]
|
||||
fn main() {
|
||||
#[rustfmt::skip]
|
||||
{
|
||||
|
@ -56,9 +33,9 @@ fn main() {
|
|||
let _ = b'B'..b'Z';
|
||||
let _ = b'1'..b'9';
|
||||
|
||||
let _ = a!()..'z';
|
||||
let _ = A!()..'Z';
|
||||
let _ = zero!()..'9';
|
||||
let _ = inline!('a')..'z';
|
||||
let _ = inline!('A')..'Z';
|
||||
let _ = inline!('0')..'9';
|
||||
|
||||
let _ = match 0u8 {
|
||||
b'a'..b'z' if true => 1,
|
||||
|
@ -80,8 +57,16 @@ fn main() {
|
|||
_ => 7,
|
||||
};
|
||||
|
||||
almost_complete_range!();
|
||||
b!();
|
||||
external!(
|
||||
let _ = 'a'..'z';
|
||||
let _ = 'A'..'Z';
|
||||
let _ = '0'..'9';
|
||||
);
|
||||
inline!(
|
||||
let _ = 'a'..'z';
|
||||
let _ = 'A'..'Z';
|
||||
let _ = '0'..'9';
|
||||
);
|
||||
}
|
||||
|
||||
#[clippy::msrv = "1.25"]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:42:17
|
||||
--> $DIR/almost_complete_range.rs:19:17
|
||||
|
|
||||
LL | let _ = ('a') ..'z';
|
||||
| ^^^^^^--^^^
|
||||
|
@ -9,7 +9,7 @@ LL | let _ = ('a') ..'z';
|
|||
= note: `-D clippy::almost-complete-range` implied by `-D warnings`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:43:17
|
||||
--> $DIR/almost_complete_range.rs:20:17
|
||||
|
|
||||
LL | let _ = 'A' .. ('Z');
|
||||
| ^^^^--^^^^^^
|
||||
|
@ -17,7 +17,7 @@ LL | let _ = 'A' .. ('Z');
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:44:17
|
||||
--> $DIR/almost_complete_range.rs:21:17
|
||||
|
|
||||
LL | let _ = ((('0'))) .. ('9');
|
||||
| ^^^^^^^^^^--^^^^^^
|
||||
|
@ -25,7 +25,7 @@ LL | let _ = ((('0'))) .. ('9');
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:51:13
|
||||
--> $DIR/almost_complete_range.rs:28:13
|
||||
|
|
||||
LL | let _ = (b'a')..(b'z');
|
||||
| ^^^^^^--^^^^^^
|
||||
|
@ -33,7 +33,7 @@ LL | let _ = (b'a')..(b'z');
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:52:13
|
||||
--> $DIR/almost_complete_range.rs:29:13
|
||||
|
|
||||
LL | let _ = b'A'..b'Z';
|
||||
| ^^^^--^^^^
|
||||
|
@ -41,7 +41,7 @@ LL | let _ = b'A'..b'Z';
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:53:13
|
||||
--> $DIR/almost_complete_range.rs:30:13
|
||||
|
|
||||
LL | let _ = b'0'..b'9';
|
||||
| ^^^^--^^^^
|
||||
|
@ -49,31 +49,31 @@ LL | let _ = b'0'..b'9';
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:59:13
|
||||
--> $DIR/almost_complete_range.rs:36:13
|
||||
|
|
||||
LL | let _ = a!()..'z';
|
||||
| ^^^^--^^^
|
||||
| |
|
||||
| help: use an inclusive range: `..=`
|
||||
LL | let _ = inline!('a')..'z';
|
||||
| ^^^^^^^^^^^^--^^^
|
||||
| |
|
||||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:60:13
|
||||
--> $DIR/almost_complete_range.rs:37:13
|
||||
|
|
||||
LL | let _ = A!()..'Z';
|
||||
| ^^^^--^^^
|
||||
| |
|
||||
| help: use an inclusive range: `..=`
|
||||
LL | let _ = inline!('A')..'Z';
|
||||
| ^^^^^^^^^^^^--^^^
|
||||
| |
|
||||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:61:13
|
||||
--> $DIR/almost_complete_range.rs:38:13
|
||||
|
|
||||
LL | let _ = zero!()..'9';
|
||||
| ^^^^^^^--^^^
|
||||
| |
|
||||
| help: use an inclusive range: `..=`
|
||||
LL | let _ = inline!('0')..'9';
|
||||
| ^^^^^^^^^^^^--^^^
|
||||
| |
|
||||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:64:9
|
||||
--> $DIR/almost_complete_range.rs:41:9
|
||||
|
|
||||
LL | b'a'..b'z' if true => 1,
|
||||
| ^^^^--^^^^
|
||||
|
@ -81,7 +81,7 @@ LL | b'a'..b'z' if true => 1,
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:65:9
|
||||
--> $DIR/almost_complete_range.rs:42:9
|
||||
|
|
||||
LL | b'A'..b'Z' if true => 2,
|
||||
| ^^^^--^^^^
|
||||
|
@ -89,7 +89,7 @@ LL | b'A'..b'Z' if true => 2,
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:66:9
|
||||
--> $DIR/almost_complete_range.rs:43:9
|
||||
|
|
||||
LL | b'0'..b'9' if true => 3,
|
||||
| ^^^^--^^^^
|
||||
|
@ -97,7 +97,7 @@ LL | b'0'..b'9' if true => 3,
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:74:9
|
||||
--> $DIR/almost_complete_range.rs:51:9
|
||||
|
|
||||
LL | 'a'..'z' if true => 1,
|
||||
| ^^^--^^^
|
||||
|
@ -105,7 +105,7 @@ LL | 'a'..'z' if true => 1,
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:75:9
|
||||
--> $DIR/almost_complete_range.rs:52:9
|
||||
|
|
||||
LL | 'A'..'Z' if true => 2,
|
||||
| ^^^--^^^
|
||||
|
@ -113,7 +113,7 @@ LL | 'A'..'Z' if true => 2,
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:76:9
|
||||
--> $DIR/almost_complete_range.rs:53:9
|
||||
|
|
||||
LL | '0'..'9' if true => 3,
|
||||
| ^^^--^^^
|
||||
|
@ -121,46 +121,37 @@ LL | '0'..'9' if true => 3,
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:33:17
|
||||
--> $DIR/almost_complete_range.rs:66:17
|
||||
|
|
||||
LL | let _ = 'a'..'z';
|
||||
| ^^^--^^^
|
||||
| |
|
||||
| help: use an inclusive range: `..=`
|
||||
...
|
||||
LL | b!();
|
||||
| ---- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:34:17
|
||||
--> $DIR/almost_complete_range.rs:67:17
|
||||
|
|
||||
LL | let _ = 'A'..'Z';
|
||||
| ^^^--^^^
|
||||
| |
|
||||
| help: use an inclusive range: `..=`
|
||||
...
|
||||
LL | b!();
|
||||
| ---- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:35:17
|
||||
--> $DIR/almost_complete_range.rs:68:17
|
||||
|
|
||||
LL | let _ = '0'..'9';
|
||||
| ^^^--^^^
|
||||
| |
|
||||
| help: use an inclusive range: `..=`
|
||||
...
|
||||
LL | b!();
|
||||
| ---- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:90:9
|
||||
--> $DIR/almost_complete_range.rs:75:9
|
||||
|
|
||||
LL | 'a'..'z' => 1,
|
||||
| ^^^--^^^
|
||||
|
@ -168,7 +159,7 @@ LL | 'a'..'z' => 1,
|
|||
| help: use an inclusive range: `...`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:91:9
|
||||
--> $DIR/almost_complete_range.rs:76:9
|
||||
|
|
||||
LL | 'A'..'Z' => 2,
|
||||
| ^^^--^^^
|
||||
|
@ -176,7 +167,7 @@ LL | 'A'..'Z' => 2,
|
|||
| help: use an inclusive range: `...`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:92:9
|
||||
--> $DIR/almost_complete_range.rs:77:9
|
||||
|
|
||||
LL | '0'..'9' => 3,
|
||||
| ^^^--^^^
|
||||
|
@ -184,7 +175,7 @@ LL | '0'..'9' => 3,
|
|||
| help: use an inclusive range: `...`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:99:13
|
||||
--> $DIR/almost_complete_range.rs:84:13
|
||||
|
|
||||
LL | let _ = 'a'..'z';
|
||||
| ^^^--^^^
|
||||
|
@ -192,7 +183,7 @@ LL | let _ = 'a'..'z';
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:100:13
|
||||
--> $DIR/almost_complete_range.rs:85:13
|
||||
|
|
||||
LL | let _ = 'A'..'Z';
|
||||
| ^^^--^^^
|
||||
|
@ -200,7 +191,7 @@ LL | let _ = 'A'..'Z';
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:101:13
|
||||
--> $DIR/almost_complete_range.rs:86:13
|
||||
|
|
||||
LL | let _ = '0'..'9';
|
||||
| ^^^--^^^
|
||||
|
@ -208,7 +199,7 @@ LL | let _ = '0'..'9';
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:103:9
|
||||
--> $DIR/almost_complete_range.rs:88:9
|
||||
|
|
||||
LL | 'a'..'z' => 1,
|
||||
| ^^^--^^^
|
||||
|
@ -216,7 +207,7 @@ LL | 'a'..'z' => 1,
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:104:9
|
||||
--> $DIR/almost_complete_range.rs:89:9
|
||||
|
|
||||
LL | 'A'..'Z' => 1,
|
||||
| ^^^--^^^
|
||||
|
@ -224,7 +215,7 @@ LL | 'A'..'Z' => 1,
|
|||
| help: use an inclusive range: `..=`
|
||||
|
||||
error: almost complete ascii range
|
||||
--> $DIR/almost_complete_range.rs:105:9
|
||||
--> $DIR/almost_complete_range.rs:90:9
|
||||
|
|
||||
LL | '0'..'9' => 3,
|
||||
| ^^^--^^^
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
// aux-build:macro_rules.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![warn(clippy::as_conversions)]
|
||||
#![allow(clippy::borrow_as_ptr)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
|
||||
fn with_external_macro() {
|
||||
as_conv_with_arg!(0u32 as u64);
|
||||
as_conv!();
|
||||
}
|
||||
extern crate proc_macros;
|
||||
use proc_macros::external;
|
||||
|
||||
fn main() {
|
||||
let i = 0u32 as u64;
|
||||
|
||||
let j = &i as *const u64 as *mut u64;
|
||||
|
||||
with_external_macro();
|
||||
external!(0u32 as u64);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: using a potentially dangerous silent `as` conversion
|
||||
--> $DIR/as_conversions.rs:15:13
|
||||
--> $DIR/as_conversions.rs:10:13
|
||||
|
|
||||
LL | let i = 0u32 as u64;
|
||||
| ^^^^^^^^^^^
|
||||
|
@ -8,7 +8,7 @@ LL | let i = 0u32 as u64;
|
|||
= note: `-D clippy::as-conversions` implied by `-D warnings`
|
||||
|
||||
error: using a potentially dangerous silent `as` conversion
|
||||
--> $DIR/as_conversions.rs:17:13
|
||||
--> $DIR/as_conversions.rs:12:13
|
||||
|
|
||||
LL | let j = &i as *const u64 as *mut u64;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -16,7 +16,7 @@ LL | let j = &i as *const u64 as *mut u64;
|
|||
= help: consider using a safe wrapper for this conversion
|
||||
|
||||
error: using a potentially dangerous silent `as` conversion
|
||||
--> $DIR/as_conversions.rs:17:13
|
||||
--> $DIR/as_conversions.rs:12:13
|
||||
|
|
||||
LL | let j = &i as *const u64 as *mut u64;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
#[macro_export]
|
||||
macro_rules! undocd_unsafe {
|
||||
() => {
|
||||
pub unsafe fn oy_vey() {
|
||||
unimplemented!();
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! undocd_safe {
|
||||
() => {
|
||||
pub fn vey_oy() {
|
||||
unimplemented!();
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
#[macro_export]
|
||||
macro_rules! implicit_hasher_fn {
|
||||
() => {
|
||||
pub fn f(input: &HashMap<u32, u32>) {}
|
||||
};
|
||||
}
|
|
@ -2,21 +2,6 @@
|
|||
|
||||
//! Used to test that certain lints don't trigger in imported external macros
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! foofoo {
|
||||
() => {
|
||||
loop {}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! must_use_unit {
|
||||
() => {
|
||||
#[must_use]
|
||||
fn foo() {}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! try_err {
|
||||
() => {
|
||||
|
@ -36,84 +21,6 @@ macro_rules! string_add {
|
|||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! take_external {
|
||||
($s:expr) => {
|
||||
std::mem::replace($s, Default::default())
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! option_env_unwrap_external {
|
||||
($env: expr) => {
|
||||
option_env!($env).unwrap()
|
||||
};
|
||||
($env: expr, $message: expr) => {
|
||||
option_env!($env).expect($message)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ref_arg_binding {
|
||||
() => {
|
||||
let ref _y = 42;
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ref_arg_function {
|
||||
() => {
|
||||
fn fun_example(ref _x: usize) {}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! as_conv_with_arg {
|
||||
(0u32 as u64) => {
|
||||
()
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! as_conv {
|
||||
() => {
|
||||
0u32 as u64
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! large_enum_variant {
|
||||
() => {
|
||||
enum LargeEnumInMacro {
|
||||
A(i32),
|
||||
B([i32; 8000]),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! field_reassign_with_default {
|
||||
() => {
|
||||
#[derive(Default)]
|
||||
struct A {
|
||||
pub i: i32,
|
||||
pub j: i64,
|
||||
}
|
||||
fn lint() {
|
||||
let mut a: A = Default::default();
|
||||
a.i = 42;
|
||||
a;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! default_numeric_fallback {
|
||||
() => {
|
||||
let x = 22;
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! mut_mut {
|
||||
() => {
|
||||
|
@ -122,49 +29,11 @@ macro_rules! mut_mut {
|
|||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ptr_as_ptr_cast {
|
||||
($ptr: ident) => {
|
||||
$ptr as *const i32
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! manual_rem_euclid {
|
||||
macro_rules! issue_10421 {
|
||||
() => {
|
||||
let value: i32 = 5;
|
||||
let _: i32 = ((value % 4) + 4) % 4;
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! equatable_if_let {
|
||||
($a:ident) => {{ if let 2 = $a {} }};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! almost_complete_range {
|
||||
() => {
|
||||
let _ = 'a'..'z';
|
||||
let _ = 'A'..'Z';
|
||||
let _ = '0'..'9';
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unsafe_macro {
|
||||
() => {
|
||||
unsafe {
|
||||
*core::ptr::null::<()>();
|
||||
*core::ptr::null::<()>();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! needless_lifetime {
|
||||
() => {
|
||||
fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 {
|
||||
unimplemented!()
|
||||
}
|
||||
let mut a = 1;
|
||||
let mut b = 2;
|
||||
a = b;
|
||||
b = a;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ pub mod inner {
|
|||
|
||||
// RE-EXPORT
|
||||
// this will stick in `inner` module
|
||||
pub use macro_rules::foofoo;
|
||||
pub use macro_rules::mut_mut;
|
||||
pub use macro_rules::try_err;
|
||||
|
||||
pub mod nested {
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
// compile-flags: --emit=link
|
||||
// no-prefer-dynamic
|
||||
|
||||
#![crate_type = "proc-macro"]
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree};
|
||||
|
||||
#[proc_macro]
|
||||
pub fn with_span(input: TokenStream) -> TokenStream {
|
||||
let mut iter = input.into_iter();
|
||||
let span = iter.next().unwrap().span();
|
||||
let mut res = TokenStream::new();
|
||||
write_with_span(span, iter, &mut res);
|
||||
res
|
||||
}
|
||||
|
||||
fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) {
|
||||
for mut tt in input {
|
||||
if let TokenTree::Group(g) = tt {
|
||||
let mut stream = TokenStream::new();
|
||||
write_with_span(s, g.stream().into_iter(), &mut stream);
|
||||
let mut group = Group::new(g.delimiter(), stream);
|
||||
group.set_span(s);
|
||||
out.extend([TokenTree::Group(group)]);
|
||||
} else {
|
||||
tt.set_span(s);
|
||||
out.extend([tt]);
|
||||
}
|
||||
}
|
||||
}
|
474
tests/ui/auxiliary/proc_macros.rs
Normal file
474
tests/ui/auxiliary/proc_macros.rs
Normal file
|
@ -0,0 +1,474 @@
|
|||
// compile-flags: --emit=link
|
||||
// no-prefer-dynamic
|
||||
|
||||
#![crate_type = "proc-macro"]
|
||||
#![feature(let_chains)]
|
||||
#![feature(proc_macro_span)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
use core::mem;
|
||||
use proc_macro::{
|
||||
token_stream::IntoIter,
|
||||
Delimiter::{self, Brace, Parenthesis},
|
||||
Group, Ident, Literal, Punct,
|
||||
Spacing::{self, Alone, Joint},
|
||||
Span, TokenStream, TokenTree as TT,
|
||||
};
|
||||
|
||||
type Result<T> = core::result::Result<T, TokenStream>;
|
||||
|
||||
/// Make a `compile_error!` pointing to the given span.
|
||||
fn make_error(msg: &str, span: Span) -> TokenStream {
|
||||
TokenStream::from_iter([
|
||||
TT::Ident(Ident::new("compile_error", span)),
|
||||
TT::Punct(punct_with_span('!', Alone, span)),
|
||||
TT::Group({
|
||||
let mut msg = Literal::string(msg);
|
||||
msg.set_span(span);
|
||||
group_with_span(Parenthesis, TokenStream::from_iter([TT::Literal(msg)]), span)
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
fn expect_tt<T>(tt: Option<TT>, f: impl FnOnce(TT) -> Option<T>, expected: &str, span: Span) -> Result<T> {
|
||||
match tt {
|
||||
None => Err(make_error(
|
||||
&format!("unexpected end of input, expected {expected}"),
|
||||
span,
|
||||
)),
|
||||
Some(tt) => {
|
||||
let span = tt.span();
|
||||
match f(tt) {
|
||||
Some(x) => Ok(x),
|
||||
None => Err(make_error(&format!("unexpected token, expected {expected}"), span)),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn punct_with_span(c: char, spacing: Spacing, span: Span) -> Punct {
|
||||
let mut p = Punct::new(c, spacing);
|
||||
p.set_span(span);
|
||||
p
|
||||
}
|
||||
|
||||
fn group_with_span(delimiter: Delimiter, stream: TokenStream, span: Span) -> Group {
|
||||
let mut g = Group::new(delimiter, stream);
|
||||
g.set_span(span);
|
||||
g
|
||||
}
|
||||
|
||||
/// Token used to escape the following token from the macro's span rules.
|
||||
const ESCAPE_CHAR: char = '$';
|
||||
|
||||
/// Takes a single token followed by a sequence tokens. Returns the sequence of tokens with their
|
||||
/// span set to that of the first token. Tokens may be escaped with either `#ident` or `#(tokens)`.
|
||||
#[proc_macro]
|
||||
pub fn with_span(input: TokenStream) -> TokenStream {
|
||||
let mut iter = input.into_iter();
|
||||
let span = iter.next().unwrap().span();
|
||||
let mut res = TokenStream::new();
|
||||
if let Err(e) = write_with_span(span, iter, &mut res) {
|
||||
e
|
||||
} else {
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes a sequence of tokens and return the tokens with the span set such that they appear to be
|
||||
/// from an external macro. Tokens may be escaped with either `#ident` or `#(tokens)`.
|
||||
#[proc_macro]
|
||||
pub fn external(input: TokenStream) -> TokenStream {
|
||||
let mut res = TokenStream::new();
|
||||
if let Err(e) = write_with_span(Span::mixed_site(), input.into_iter(), &mut res) {
|
||||
e
|
||||
} else {
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies all the tokens, replacing all their spans with the given span. Tokens can be escaped
|
||||
/// either by `#ident` or `#(tokens)`.
|
||||
fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Result<()> {
|
||||
while let Some(tt) = input.next() {
|
||||
match tt {
|
||||
TT::Punct(p) if p.as_char() == ESCAPE_CHAR => {
|
||||
expect_tt(
|
||||
input.next(),
|
||||
|tt| match tt {
|
||||
tt @ (TT::Ident(_) | TT::Literal(_)) => {
|
||||
out.extend([tt]);
|
||||
Some(())
|
||||
},
|
||||
TT::Punct(mut p) if p.as_char() == ESCAPE_CHAR => {
|
||||
p.set_span(s);
|
||||
out.extend([TT::Punct(p)]);
|
||||
Some(())
|
||||
},
|
||||
TT::Group(g) if g.delimiter() == Parenthesis => {
|
||||
out.extend([TT::Group(group_with_span(Delimiter::None, g.stream(), g.span()))]);
|
||||
Some(())
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
"an ident, a literal, or parenthesized tokens",
|
||||
p.span(),
|
||||
)?;
|
||||
},
|
||||
TT::Group(g) => {
|
||||
let mut stream = TokenStream::new();
|
||||
write_with_span(s, g.stream().into_iter(), &mut stream)?;
|
||||
out.extend([TT::Group(group_with_span(g.delimiter(), stream, s))]);
|
||||
},
|
||||
mut tt => {
|
||||
tt.set_span(s);
|
||||
out.extend([tt]);
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Within the item this attribute is attached to, an `inline!` macro is available which expands the
|
||||
/// contained tokens as though they came from a macro expansion.
|
||||
///
|
||||
/// Within the `inline!` macro, any token preceded by `$` is passed as though it were an argument
|
||||
/// with an automatically chosen fragment specifier. `$ident` will be passed as `ident`, `$1` or
|
||||
/// `$"literal"` will be passed as `literal`, `$'lt` will be passed as `lifetime`, and `$(...)` will
|
||||
/// pass the contained tokens as a `tt` sequence (the wrapping parenthesis are removed). If another
|
||||
/// specifier is required it can be specified within parenthesis like `$(@expr ...)`. This will
|
||||
/// expand the remaining tokens as a single argument.
|
||||
///
|
||||
/// Multiple `inline!` macros may be nested within each other. This will expand as nested macro
|
||||
/// calls. However, any arguments will be passed as though they came from the outermost context.
|
||||
#[proc_macro_attribute]
|
||||
pub fn inline_macros(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mut args = args.into_iter();
|
||||
let mac_name = match args.next() {
|
||||
Some(TT::Ident(name)) => Some(name),
|
||||
Some(tt) => {
|
||||
return make_error(
|
||||
"unexpected argument, expected either an ident or no arguments",
|
||||
tt.span(),
|
||||
);
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
if let Some(tt) = args.next() {
|
||||
return make_error(
|
||||
"unexpected argument, expected either an ident or no arguments",
|
||||
tt.span(),
|
||||
);
|
||||
};
|
||||
|
||||
let mac_name = if let Some(mac_name) = mac_name {
|
||||
Ident::new(&format!("__inline_mac_{mac_name}"), Span::call_site())
|
||||
} else {
|
||||
let mut input = match LookaheadIter::new(input.clone().into_iter()) {
|
||||
Some(x) => x,
|
||||
None => return input,
|
||||
};
|
||||
loop {
|
||||
match input.next() {
|
||||
None => break Ident::new("__inline_mac", Span::call_site()),
|
||||
Some(TT::Ident(kind)) => match &*kind.to_string() {
|
||||
"impl" => break Ident::new("__inline_mac_impl", Span::call_site()),
|
||||
kind @ ("struct" | "enum" | "union" | "fn" | "mod" | "trait" | "type" | "const" | "static") => {
|
||||
if let TT::Ident(name) = &input.tt {
|
||||
break Ident::new(&format!("__inline_mac_{kind}_{name}"), Span::call_site());
|
||||
} else {
|
||||
break Ident::new(&format!("__inline_mac_{kind}"), Span::call_site());
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut expander = Expander::default();
|
||||
let mut mac = MacWriter::new(mac_name);
|
||||
if let Err(e) = expander.expand(input.into_iter(), &mut mac) {
|
||||
return e;
|
||||
}
|
||||
let mut out = TokenStream::new();
|
||||
mac.finish(&mut out);
|
||||
out.extend(expander.expn);
|
||||
out
|
||||
}
|
||||
|
||||
/// Wraps a `TokenStream` iterator with a single token lookahead.
|
||||
struct LookaheadIter {
|
||||
tt: TT,
|
||||
iter: IntoIter,
|
||||
}
|
||||
impl LookaheadIter {
|
||||
fn new(mut iter: IntoIter) -> Option<Self> {
|
||||
iter.next().map(|tt| Self { tt, iter })
|
||||
}
|
||||
|
||||
/// Get's the lookahead token, replacing it with the next token in the stream.
|
||||
/// Note: If there isn't a next token, this will not return the lookahead token.
|
||||
fn next(&mut self) -> Option<TT> {
|
||||
self.iter.next().map(|tt| mem::replace(&mut self.tt, tt))
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the macro used to implement all the `inline!` macro calls.
|
||||
struct MacWriter {
|
||||
name: Ident,
|
||||
macros: TokenStream,
|
||||
next_idx: usize,
|
||||
}
|
||||
impl MacWriter {
|
||||
fn new(name: Ident) -> Self {
|
||||
Self {
|
||||
name,
|
||||
macros: TokenStream::new(),
|
||||
next_idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts a new `inline!` call.
|
||||
fn insert(&mut self, name_span: Span, bang_span: Span, body: Group, expander: &mut Expander) -> Result<()> {
|
||||
let idx = self.next_idx;
|
||||
self.next_idx += 1;
|
||||
|
||||
let mut inner = Expander::for_arm(idx);
|
||||
inner.expand(body.stream().into_iter(), self)?;
|
||||
let new_arm = inner.arm.unwrap();
|
||||
|
||||
self.macros.extend([
|
||||
TT::Group(Group::new(Parenthesis, new_arm.args_def)),
|
||||
TT::Punct(Punct::new('=', Joint)),
|
||||
TT::Punct(Punct::new('>', Alone)),
|
||||
TT::Group(Group::new(Parenthesis, inner.expn)),
|
||||
TT::Punct(Punct::new(';', Alone)),
|
||||
]);
|
||||
|
||||
expander.expn.extend([
|
||||
TT::Ident({
|
||||
let mut name = self.name.clone();
|
||||
name.set_span(name_span);
|
||||
name
|
||||
}),
|
||||
TT::Punct(punct_with_span('!', Alone, bang_span)),
|
||||
]);
|
||||
let mut call_body = TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]);
|
||||
if let Some(arm) = expander.arm.as_mut() {
|
||||
if !new_arm.args.is_empty() {
|
||||
arm.add_sub_args(new_arm.args, &mut call_body);
|
||||
}
|
||||
} else {
|
||||
call_body.extend(new_arm.args);
|
||||
}
|
||||
let mut g = Group::new(body.delimiter(), call_body);
|
||||
g.set_span(body.span());
|
||||
expander.expn.extend([TT::Group(g)]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates the macro definition.
|
||||
fn finish(self, out: &mut TokenStream) {
|
||||
if self.next_idx != 0 {
|
||||
out.extend([
|
||||
TT::Ident(Ident::new("macro_rules", Span::call_site())),
|
||||
TT::Punct(Punct::new('!', Alone)),
|
||||
TT::Ident(self.name),
|
||||
TT::Group(Group::new(Brace, self.macros)),
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MacroArm {
|
||||
args_def: TokenStream,
|
||||
args: Vec<TT>,
|
||||
}
|
||||
impl MacroArm {
|
||||
fn add_single_arg_def(&mut self, kind: &str, dollar_span: Span, arg_span: Span, out: &mut TokenStream) {
|
||||
let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site());
|
||||
self.args_def.extend([
|
||||
TT::Punct(Punct::new('$', Alone)),
|
||||
TT::Ident(name.clone()),
|
||||
TT::Punct(Punct::new(':', Alone)),
|
||||
TT::Ident(Ident::new(kind, Span::call_site())),
|
||||
]);
|
||||
name.set_span(arg_span);
|
||||
out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]);
|
||||
}
|
||||
|
||||
fn add_parenthesized_arg_def(&mut self, kind: Ident, dollar_span: Span, arg_span: Span, out: &mut TokenStream) {
|
||||
let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site());
|
||||
self.args_def.extend([TT::Group(Group::new(
|
||||
Parenthesis,
|
||||
TokenStream::from_iter([
|
||||
TT::Punct(Punct::new('$', Alone)),
|
||||
TT::Ident(name.clone()),
|
||||
TT::Punct(Punct::new(':', Alone)),
|
||||
TT::Ident(kind),
|
||||
]),
|
||||
))]);
|
||||
name.set_span(arg_span);
|
||||
out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]);
|
||||
}
|
||||
|
||||
fn add_multi_arg_def(&mut self, dollar_span: Span, arg_span: Span, out: &mut TokenStream) {
|
||||
let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site());
|
||||
self.args_def.extend([TT::Group(Group::new(
|
||||
Parenthesis,
|
||||
TokenStream::from_iter([
|
||||
TT::Punct(Punct::new('$', Alone)),
|
||||
TT::Group(Group::new(
|
||||
Parenthesis,
|
||||
TokenStream::from_iter([
|
||||
TT::Punct(Punct::new('$', Alone)),
|
||||
TT::Ident(name.clone()),
|
||||
TT::Punct(Punct::new(':', Alone)),
|
||||
TT::Ident(Ident::new("tt", Span::call_site())),
|
||||
]),
|
||||
)),
|
||||
TT::Punct(Punct::new('*', Alone)),
|
||||
]),
|
||||
))]);
|
||||
name.set_span(arg_span);
|
||||
out.extend([
|
||||
TT::Punct(punct_with_span('$', Alone, dollar_span)),
|
||||
TT::Group(group_with_span(
|
||||
Parenthesis,
|
||||
TokenStream::from_iter([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]),
|
||||
dollar_span,
|
||||
)),
|
||||
TT::Punct(punct_with_span('*', Alone, dollar_span)),
|
||||
]);
|
||||
}
|
||||
|
||||
fn add_arg(&mut self, dollar_span: Span, tt: TT, input: &mut IntoIter, out: &mut TokenStream) -> Result<()> {
|
||||
match tt {
|
||||
TT::Punct(p) if p.as_char() == ESCAPE_CHAR => out.extend([TT::Punct(p)]),
|
||||
TT::Punct(p) if p.as_char() == '\'' && p.spacing() == Joint => {
|
||||
let lt_name = expect_tt(
|
||||
input.next(),
|
||||
|tt| match tt {
|
||||
TT::Ident(x) => Some(x),
|
||||
_ => None,
|
||||
},
|
||||
"lifetime name",
|
||||
p.span(),
|
||||
)?;
|
||||
let arg_span = p.span().join(lt_name.span()).unwrap_or(p.span());
|
||||
self.add_single_arg_def("lifetime", dollar_span, arg_span, out);
|
||||
self.args.extend([TT::Punct(p), TT::Ident(lt_name)]);
|
||||
},
|
||||
TT::Ident(x) => {
|
||||
self.add_single_arg_def("ident", dollar_span, x.span(), out);
|
||||
self.args.push(TT::Ident(x));
|
||||
},
|
||||
TT::Literal(x) => {
|
||||
self.add_single_arg_def("literal", dollar_span, x.span(), out);
|
||||
self.args.push(TT::Literal(x));
|
||||
},
|
||||
TT::Group(g) if g.delimiter() == Parenthesis => {
|
||||
let mut inner = g.stream().into_iter();
|
||||
if let Some(TT::Punct(p)) = inner.next()
|
||||
&& p.as_char() == '@'
|
||||
{
|
||||
let kind = expect_tt(
|
||||
inner.next(),
|
||||
|tt| match tt {
|
||||
TT::Ident(kind) => Some(kind),
|
||||
_ => None,
|
||||
},
|
||||
"a macro fragment specifier",
|
||||
p.span(),
|
||||
)?;
|
||||
self.add_parenthesized_arg_def(kind, dollar_span, g.span(), out);
|
||||
self.args.push(TT::Group(group_with_span(Parenthesis, inner.collect(), g.span())))
|
||||
} else {
|
||||
self.add_multi_arg_def(dollar_span, g.span(), out);
|
||||
self.args.push(TT::Group(g));
|
||||
}
|
||||
},
|
||||
tt => return Err(make_error("unsupported escape", tt.span())),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_sub_args(&mut self, args: Vec<TT>, out: &mut TokenStream) {
|
||||
self.add_multi_arg_def(Span::call_site(), Span::call_site(), out);
|
||||
self.args
|
||||
.extend([TT::Group(Group::new(Parenthesis, TokenStream::from_iter(args)))]);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Expander {
|
||||
arm: Option<MacroArm>,
|
||||
expn: TokenStream,
|
||||
}
|
||||
impl Expander {
|
||||
fn for_arm(idx: usize) -> Self {
|
||||
Self {
|
||||
arm: Some(MacroArm {
|
||||
args_def: TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]),
|
||||
args: Vec::new(),
|
||||
}),
|
||||
expn: TokenStream::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_tt(&mut self, tt: TT, mac: &mut MacWriter) -> Result<()> {
|
||||
match tt {
|
||||
TT::Group(g) => {
|
||||
let outer = mem::take(&mut self.expn);
|
||||
self.expand(g.stream().into_iter(), mac)?;
|
||||
let inner = mem::replace(&mut self.expn, outer);
|
||||
self.expn
|
||||
.extend([TT::Group(group_with_span(g.delimiter(), inner, g.span()))]);
|
||||
},
|
||||
tt => self.expn.extend([tt]),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand(&mut self, input: IntoIter, mac: &mut MacWriter) -> Result<()> {
|
||||
let Some(mut input) = LookaheadIter::new(input) else {
|
||||
return Ok(());
|
||||
};
|
||||
while let Some(tt) = input.next() {
|
||||
if let TT::Punct(p) = &tt
|
||||
&& p.as_char() == ESCAPE_CHAR
|
||||
&& let Some(arm) = self.arm.as_mut()
|
||||
{
|
||||
arm.add_arg(p.span(), mem::replace(&mut input.tt, tt), &mut input.iter, &mut self.expn)?;
|
||||
if input.next().is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
} else if let TT::Punct(p) = &input.tt
|
||||
&& p.as_char() == '!'
|
||||
&& let TT::Ident(name) = &tt
|
||||
&& name.to_string() == "inline"
|
||||
{
|
||||
let g = expect_tt(
|
||||
input.iter.next(),
|
||||
|tt| match tt {
|
||||
TT::Group(g) => Some(g),
|
||||
_ => None,
|
||||
},
|
||||
"macro arguments",
|
||||
p.span(),
|
||||
)?;
|
||||
mac.insert(name.span(), p.span(), g, self)?;
|
||||
if input.next().is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
self.write_tt(tt, mac)?;
|
||||
}
|
||||
}
|
||||
self.write_tt(input.tt, mac)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// this file solely exists to test constants defined in foreign crates.
|
||||
// As the most common case is the `http` crate, it replicates `http::HeadewrName`'s structure.
|
||||
// As the most common case is the `http` crate, it replicates `http::HeaderName`'s structure.
|
||||
|
||||
#![allow(clippy::declare_interior_mutable_const)]
|
||||
#![allow(unused_tuple_struct_fields)]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// aux-build:../../auxiliary/proc_macro_with_span.rs
|
||||
// aux-build:../../auxiliary/proc_macros.rs
|
||||
|
||||
extern crate proc_macro_with_span;
|
||||
extern crate proc_macros;
|
||||
|
||||
use proc_macro_with_span::with_span;
|
||||
use proc_macros::with_span;
|
||||
|
||||
fn main() {
|
||||
println!(with_span!(""something ""));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// run-rustfix
|
||||
// aux-build:macro_rules.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![warn(clippy::default_numeric_fallback)]
|
||||
#![allow(
|
||||
|
@ -13,8 +13,8 @@
|
|||
clippy::let_with_type_underscore
|
||||
)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
extern crate proc_macros;
|
||||
use proc_macros::{external, inline_macros};
|
||||
|
||||
mod basic_expr {
|
||||
fn test() {
|
||||
|
@ -167,20 +167,17 @@ mod method_calls {
|
|||
}
|
||||
|
||||
mod in_macro {
|
||||
macro_rules! internal_macro {
|
||||
() => {
|
||||
let x = 22.0_f64;
|
||||
};
|
||||
}
|
||||
use super::*;
|
||||
|
||||
// Should lint in internal macro.
|
||||
#[inline_macros]
|
||||
fn internal() {
|
||||
internal_macro!();
|
||||
inline!(let x = 22.0_f64;);
|
||||
}
|
||||
|
||||
// Should NOT lint in external macro.
|
||||
fn external() {
|
||||
default_numeric_fallback!();
|
||||
external!(let x = 22.;);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// run-rustfix
|
||||
// aux-build:macro_rules.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![warn(clippy::default_numeric_fallback)]
|
||||
#![allow(
|
||||
|
@ -13,8 +13,8 @@
|
|||
clippy::let_with_type_underscore
|
||||
)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
extern crate proc_macros;
|
||||
use proc_macros::{external, inline_macros};
|
||||
|
||||
mod basic_expr {
|
||||
fn test() {
|
||||
|
@ -167,20 +167,17 @@ mod method_calls {
|
|||
}
|
||||
|
||||
mod in_macro {
|
||||
macro_rules! internal_macro {
|
||||
() => {
|
||||
let x = 22.;
|
||||
};
|
||||
}
|
||||
use super::*;
|
||||
|
||||
// Should lint in internal macro.
|
||||
#[inline_macros]
|
||||
fn internal() {
|
||||
internal_macro!();
|
||||
inline!(let x = 22.;);
|
||||
}
|
||||
|
||||
// Should NOT lint in external macro.
|
||||
fn external() {
|
||||
default_numeric_fallback!();
|
||||
external!(let x = 22.;);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -139,15 +139,12 @@ LL | s.generic_arg(1.);
|
|||
| ^^ help: consider adding suffix: `1.0_f64`
|
||||
|
||||
error: default numeric fallback might occur
|
||||
--> $DIR/default_numeric_fallback_f64.rs:172:21
|
||||
--> $DIR/default_numeric_fallback_f64.rs:175:25
|
||||
|
|
||||
LL | let x = 22.;
|
||||
| ^^^ help: consider adding suffix: `22.0_f64`
|
||||
...
|
||||
LL | internal_macro!();
|
||||
| ----------------- in this macro invocation
|
||||
LL | inline!(let x = 22.;);
|
||||
| ^^^ help: consider adding suffix: `22.0_f64`
|
||||
|
|
||||
= note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 24 previous errors
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// run-rustfix
|
||||
// aux-build:macro_rules.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![feature(lint_reasons)]
|
||||
#![warn(clippy::default_numeric_fallback)]
|
||||
|
@ -13,8 +13,8 @@
|
|||
clippy::let_with_type_underscore
|
||||
)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
extern crate proc_macros;
|
||||
use proc_macros::{external, inline_macros};
|
||||
|
||||
mod basic_expr {
|
||||
fn test() {
|
||||
|
@ -168,20 +168,17 @@ mod method_calls {
|
|||
}
|
||||
|
||||
mod in_macro {
|
||||
macro_rules! internal_macro {
|
||||
() => {
|
||||
let x = 22_i32;
|
||||
};
|
||||
}
|
||||
use super::*;
|
||||
|
||||
// Should lint in internal macro.
|
||||
#[inline_macros]
|
||||
fn internal() {
|
||||
internal_macro!();
|
||||
inline!(let x = 22_i32;);
|
||||
}
|
||||
|
||||
// Should NOT lint in external macro.
|
||||
fn external() {
|
||||
default_numeric_fallback!();
|
||||
external!(let x = 22;);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// run-rustfix
|
||||
// aux-build:macro_rules.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![feature(lint_reasons)]
|
||||
#![warn(clippy::default_numeric_fallback)]
|
||||
|
@ -13,8 +13,8 @@
|
|||
clippy::let_with_type_underscore
|
||||
)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
extern crate proc_macros;
|
||||
use proc_macros::{external, inline_macros};
|
||||
|
||||
mod basic_expr {
|
||||
fn test() {
|
||||
|
@ -168,20 +168,17 @@ mod method_calls {
|
|||
}
|
||||
|
||||
mod in_macro {
|
||||
macro_rules! internal_macro {
|
||||
() => {
|
||||
let x = 22;
|
||||
};
|
||||
}
|
||||
use super::*;
|
||||
|
||||
// Should lint in internal macro.
|
||||
#[inline_macros]
|
||||
fn internal() {
|
||||
internal_macro!();
|
||||
inline!(let x = 22;);
|
||||
}
|
||||
|
||||
// Should NOT lint in external macro.
|
||||
fn external() {
|
||||
default_numeric_fallback!();
|
||||
external!(let x = 22;);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -151,15 +151,12 @@ LL | s.generic_arg(1);
|
|||
| ^ help: consider adding suffix: `1_i32`
|
||||
|
||||
error: default numeric fallback might occur
|
||||
--> $DIR/default_numeric_fallback_i32.rs:173:21
|
||||
--> $DIR/default_numeric_fallback_i32.rs:176:25
|
||||
|
|
||||
LL | let x = 22;
|
||||
| ^^ help: consider adding suffix: `22_i32`
|
||||
...
|
||||
LL | internal_macro!();
|
||||
| ----------------- in this macro invocation
|
||||
LL | inline!(let x = 22;);
|
||||
| ^^ help: consider adding suffix: `22_i32`
|
||||
|
|
||||
= note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 26 previous errors
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// run-rustfix
|
||||
// aux-build: proc_macro_with_span.rs
|
||||
// aux-build: proc_macros.rs
|
||||
#![deny(clippy::default_trait_access)]
|
||||
#![allow(dead_code, unused_imports)]
|
||||
#![allow(clippy::uninlined_format_args)]
|
||||
|
||||
extern crate proc_macro_with_span;
|
||||
extern crate proc_macros;
|
||||
|
||||
use proc_macro_with_span::with_span;
|
||||
use proc_macros::with_span;
|
||||
use std::default;
|
||||
use std::default::Default as D2;
|
||||
use std::string;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// run-rustfix
|
||||
// aux-build: proc_macro_with_span.rs
|
||||
// aux-build: proc_macros.rs
|
||||
#![deny(clippy::default_trait_access)]
|
||||
#![allow(dead_code, unused_imports)]
|
||||
#![allow(clippy::uninlined_format_args)]
|
||||
|
||||
extern crate proc_macro_with_span;
|
||||
extern crate proc_macros;
|
||||
|
||||
use proc_macro_with_span::with_span;
|
||||
use proc_macros::with_span;
|
||||
use std::default;
|
||||
use std::default::Default as D2;
|
||||
use std::string;
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
// run-rustfix
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![allow(clippy::return_self_not_must_use)]
|
||||
#![warn(clippy::deref_addrof)]
|
||||
|
||||
extern crate proc_macros;
|
||||
use proc_macros::inline_macros;
|
||||
|
||||
fn get_number() -> usize {
|
||||
10
|
||||
}
|
||||
|
@ -41,28 +46,15 @@ fn main() {
|
|||
let _ = unsafe { *core::ptr::addr_of!(a) };
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
macro_rules! m {
|
||||
($visitor: expr) => {
|
||||
$visitor
|
||||
};
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
macro_rules! m_mut {
|
||||
($visitor: expr) => {
|
||||
$visitor
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct S;
|
||||
#[inline_macros]
|
||||
impl S {
|
||||
pub fn f(&self) -> &Self {
|
||||
m!(self)
|
||||
inline!($(@expr self))
|
||||
}
|
||||
#[allow(unused_mut)] // mut will be unused, once the macro is fixed
|
||||
pub fn f_mut(mut self) -> Self {
|
||||
m_mut!(self)
|
||||
inline!($(@expr self))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
// run-rustfix
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![allow(clippy::return_self_not_must_use)]
|
||||
#![warn(clippy::deref_addrof)]
|
||||
|
||||
extern crate proc_macros;
|
||||
use proc_macros::inline_macros;
|
||||
|
||||
fn get_number() -> usize {
|
||||
10
|
||||
}
|
||||
|
@ -41,28 +46,15 @@ fn main() {
|
|||
let _ = unsafe { *core::ptr::addr_of!(a) };
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
macro_rules! m {
|
||||
($visitor: expr) => {
|
||||
*& $visitor
|
||||
};
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
macro_rules! m_mut {
|
||||
($visitor: expr) => {
|
||||
*& mut $visitor
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct S;
|
||||
#[inline_macros]
|
||||
impl S {
|
||||
pub fn f(&self) -> &Self {
|
||||
m!(self)
|
||||
inline!(*& $(@expr self))
|
||||
}
|
||||
#[allow(unused_mut)] // mut will be unused, once the macro is fixed
|
||||
pub fn f_mut(mut self) -> Self {
|
||||
m_mut!(self)
|
||||
inline!(*&mut $(@expr self))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: immediately dereferencing a reference
|
||||
--> $DIR/deref_addrof.rs:19:13
|
||||
--> $DIR/deref_addrof.rs:24:13
|
||||
|
|
||||
LL | let b = *&a;
|
||||
| ^^^ help: try this: `a`
|
||||
|
@ -7,68 +7,62 @@ LL | let b = *&a;
|
|||
= note: `-D clippy::deref-addrof` implied by `-D warnings`
|
||||
|
||||
error: immediately dereferencing a reference
|
||||
--> $DIR/deref_addrof.rs:21:13
|
||||
--> $DIR/deref_addrof.rs:26:13
|
||||
|
|
||||
LL | let b = *&get_number();
|
||||
| ^^^^^^^^^^^^^^ help: try this: `get_number()`
|
||||
|
||||
error: immediately dereferencing a reference
|
||||
--> $DIR/deref_addrof.rs:26:13
|
||||
--> $DIR/deref_addrof.rs:31:13
|
||||
|
|
||||
LL | let b = *&bytes[1..2][0];
|
||||
| ^^^^^^^^^^^^^^^^ help: try this: `bytes[1..2][0]`
|
||||
|
||||
error: immediately dereferencing a reference
|
||||
--> $DIR/deref_addrof.rs:30:13
|
||||
--> $DIR/deref_addrof.rs:35:13
|
||||
|
|
||||
LL | let b = *&(a);
|
||||
| ^^^^^ help: try this: `(a)`
|
||||
|
||||
error: immediately dereferencing a reference
|
||||
--> $DIR/deref_addrof.rs:32:13
|
||||
--> $DIR/deref_addrof.rs:37:13
|
||||
|
|
||||
LL | let b = *(&a);
|
||||
| ^^^^^ help: try this: `a`
|
||||
|
||||
error: immediately dereferencing a reference
|
||||
--> $DIR/deref_addrof.rs:35:13
|
||||
--> $DIR/deref_addrof.rs:40:13
|
||||
|
|
||||
LL | let b = *((&a));
|
||||
| ^^^^^^^ help: try this: `a`
|
||||
|
||||
error: immediately dereferencing a reference
|
||||
--> $DIR/deref_addrof.rs:37:13
|
||||
--> $DIR/deref_addrof.rs:42:13
|
||||
|
|
||||
LL | let b = *&&a;
|
||||
| ^^^^ help: try this: `&a`
|
||||
|
||||
error: immediately dereferencing a reference
|
||||
--> $DIR/deref_addrof.rs:39:14
|
||||
--> $DIR/deref_addrof.rs:44:14
|
||||
|
|
||||
LL | let b = **&aref;
|
||||
| ^^^^^^ help: try this: `aref`
|
||||
|
||||
error: immediately dereferencing a reference
|
||||
--> $DIR/deref_addrof.rs:47:9
|
||||
--> $DIR/deref_addrof.rs:54:17
|
||||
|
|
||||
LL | *& $visitor
|
||||
| ^^^^^^^^^^^ help: try this: `$visitor`
|
||||
...
|
||||
LL | m!(self)
|
||||
| -------- in this macro invocation
|
||||
LL | inline!(*& $(@expr self))
|
||||
| ^^^^^^^^^^^^^^^^ help: try this: `$(@expr self)`
|
||||
|
|
||||
= note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: immediately dereferencing a reference
|
||||
--> $DIR/deref_addrof.rs:54:9
|
||||
--> $DIR/deref_addrof.rs:58:17
|
||||
|
|
||||
LL | *& mut $visitor
|
||||
| ^^^^^^^^^^^^^^^ help: try this: `$visitor`
|
||||
...
|
||||
LL | m_mut!(self)
|
||||
| ------------ in this macro invocation
|
||||
LL | inline!(*&mut $(@expr self))
|
||||
| ^^^^^^^^^^^^^^^^^^^ help: try this: `$(@expr self)`
|
||||
|
|
||||
= note: this error originates in the macro `m_mut` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
macro_rules! m {
|
||||
($($x:tt),*) => { &[$(($x, stringify!(x)),)*] };
|
||||
}
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#[warn(clippy::deref_addrof)]
|
||||
fn f() -> [(i32, &'static str); 3] {
|
||||
*m![1, 2, 3] // should be fine
|
||||
#![warn(clippy::deref_addrof)]
|
||||
|
||||
extern crate proc_macros;
|
||||
|
||||
#[proc_macros::inline_macros]
|
||||
fn f() -> i32 {
|
||||
// should be fine
|
||||
*inline!(&$1)
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// aux-build:doc_unsafe_macros.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![allow(clippy::let_unit_value)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate doc_unsafe_macros;
|
||||
extern crate proc_macros;
|
||||
use proc_macros::external;
|
||||
|
||||
/// This is not sufficiently documented
|
||||
pub unsafe fn destroy_the_planet() {
|
||||
|
@ -105,7 +105,11 @@ macro_rules! very_unsafe {
|
|||
very_unsafe!();
|
||||
|
||||
// we don't lint code from external macros
|
||||
undocd_unsafe!();
|
||||
external! {
|
||||
pub unsafe fn oy_vey() {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// aux-build:macro_rules.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![warn(clippy::empty_loop)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
extern crate proc_macros;
|
||||
use proc_macros::{external, inline_macros};
|
||||
|
||||
fn should_trigger() {
|
||||
loop {}
|
||||
|
@ -16,6 +16,7 @@ fn should_trigger() {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline_macros]
|
||||
fn should_not_trigger() {
|
||||
loop {
|
||||
panic!("This is fine")
|
||||
|
@ -38,14 +39,10 @@ fn should_not_trigger() {
|
|||
loop {}
|
||||
|
||||
// We don't lint loops inside macros
|
||||
macro_rules! foo {
|
||||
() => {
|
||||
loop {}
|
||||
};
|
||||
}
|
||||
inline!(loop {});
|
||||
|
||||
// We don't lint external macros
|
||||
foofoo!()
|
||||
external!(loop {});
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// run-rustfix
|
||||
// aux-build:macro_rules.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
|
||||
#![warn(clippy::equatable_if_let)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
extern crate proc_macros;
|
||||
use proc_macros::{external, inline_macros};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
|
@ -44,6 +44,7 @@ impl PartialEq for NotStructuralEq {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline_macros]
|
||||
fn main() {
|
||||
let a = 2;
|
||||
let b = 3;
|
||||
|
@ -78,14 +79,9 @@ fn main() {
|
|||
if Some(g) == Some(NotStructuralEq::A) {}
|
||||
if matches!(h, NoPartialEqStruct { a: 2, b: false }) {}
|
||||
|
||||
macro_rules! m1 {
|
||||
(x) => {
|
||||
"abc"
|
||||
};
|
||||
}
|
||||
if "abc" == m1!(x) {
|
||||
if "abc" == inline!("abc") {
|
||||
println!("OK");
|
||||
}
|
||||
|
||||
equatable_if_let!(a);
|
||||
external!({ if let 2 = $a {} });
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// run-rustfix
|
||||
// aux-build:macro_rules.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
|
||||
#![warn(clippy::equatable_if_let)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
extern crate proc_macros;
|
||||
use proc_macros::{external, inline_macros};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
|
@ -44,6 +44,7 @@ impl PartialEq for NotStructuralEq {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline_macros]
|
||||
fn main() {
|
||||
let a = 2;
|
||||
let b = 3;
|
||||
|
@ -78,14 +79,9 @@ fn main() {
|
|||
if let Some(NotStructuralEq::A) = Some(g) {}
|
||||
if let NoPartialEqStruct { a: 2, b: false } = h {}
|
||||
|
||||
macro_rules! m1 {
|
||||
(x) => {
|
||||
"abc"
|
||||
};
|
||||
}
|
||||
if let m1!(x) = "abc" {
|
||||
if let inline!("abc") = "abc" {
|
||||
println!("OK");
|
||||
}
|
||||
|
||||
equatable_if_let!(a);
|
||||
external!({ if let 2 = $a {} });
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: this pattern matching can be expressed using equality
|
||||
--> $DIR/equatable_if_let.rs:59:8
|
||||
--> $DIR/equatable_if_let.rs:60:8
|
||||
|
|
||||
LL | if let 2 = a {}
|
||||
| ^^^^^^^^^ help: try: `a == 2`
|
||||
|
@ -7,82 +7,82 @@ LL | if let 2 = a {}
|
|||
= note: `-D clippy::equatable-if-let` implied by `-D warnings`
|
||||
|
||||
error: this pattern matching can be expressed using equality
|
||||
--> $DIR/equatable_if_let.rs:60:8
|
||||
--> $DIR/equatable_if_let.rs:61:8
|
||||
|
|
||||
LL | if let Ordering::Greater = a.cmp(&b) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater`
|
||||
|
||||
error: this pattern matching can be expressed using equality
|
||||
--> $DIR/equatable_if_let.rs:61:8
|
||||
--> $DIR/equatable_if_let.rs:62:8
|
||||
|
|
||||
LL | if let Some(2) = c {}
|
||||
| ^^^^^^^^^^^^^^^ help: try: `c == Some(2)`
|
||||
|
||||
error: this pattern matching can be expressed using equality
|
||||
--> $DIR/equatable_if_let.rs:62:8
|
||||
--> $DIR/equatable_if_let.rs:63:8
|
||||
|
|
||||
LL | if let Struct { a: 2, b: false } = d {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })`
|
||||
|
||||
error: this pattern matching can be expressed using equality
|
||||
--> $DIR/equatable_if_let.rs:63:8
|
||||
--> $DIR/equatable_if_let.rs:64:8
|
||||
|
|
||||
LL | if let Enum::TupleVariant(32, 64) = e {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)`
|
||||
|
||||
error: this pattern matching can be expressed using equality
|
||||
--> $DIR/equatable_if_let.rs:64:8
|
||||
--> $DIR/equatable_if_let.rs:65:8
|
||||
|
|
||||
LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })`
|
||||
|
||||
error: this pattern matching can be expressed using equality
|
||||
--> $DIR/equatable_if_let.rs:65:8
|
||||
--> $DIR/equatable_if_let.rs:66:8
|
||||
|
|
||||
LL | if let Enum::UnitVariant = e {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant`
|
||||
|
||||
error: this pattern matching can be expressed using equality
|
||||
--> $DIR/equatable_if_let.rs:66:8
|
||||
--> $DIR/equatable_if_let.rs:67:8
|
||||
|
|
||||
LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })`
|
||||
|
||||
error: this pattern matching can be expressed using `matches!`
|
||||
--> $DIR/equatable_if_let.rs:75:8
|
||||
--> $DIR/equatable_if_let.rs:76:8
|
||||
|
|
||||
LL | if let NotPartialEq::A = f {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(f, NotPartialEq::A)`
|
||||
|
||||
error: this pattern matching can be expressed using equality
|
||||
--> $DIR/equatable_if_let.rs:76:8
|
||||
--> $DIR/equatable_if_let.rs:77:8
|
||||
|
|
||||
LL | if let NotStructuralEq::A = g {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A`
|
||||
|
||||
error: this pattern matching can be expressed using `matches!`
|
||||
--> $DIR/equatable_if_let.rs:77:8
|
||||
--> $DIR/equatable_if_let.rs:78:8
|
||||
|
|
||||
LL | if let Some(NotPartialEq::A) = Some(f) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(f), Some(NotPartialEq::A))`
|
||||
|
||||
error: this pattern matching can be expressed using equality
|
||||
--> $DIR/equatable_if_let.rs:78:8
|
||||
--> $DIR/equatable_if_let.rs:79:8
|
||||
|
|
||||
LL | if let Some(NotStructuralEq::A) = Some(g) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)`
|
||||
|
||||
error: this pattern matching can be expressed using `matches!`
|
||||
--> $DIR/equatable_if_let.rs:79:8
|
||||
--> $DIR/equatable_if_let.rs:80:8
|
||||
|
|
||||
LL | if let NoPartialEqStruct { a: 2, b: false } = h {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(h, NoPartialEqStruct { a: 2, b: false })`
|
||||
|
||||
error: this pattern matching can be expressed using equality
|
||||
--> $DIR/equatable_if_let.rs:86:8
|
||||
--> $DIR/equatable_if_let.rs:82:8
|
||||
|
|
||||
LL | if let m1!(x) = "abc" {
|
||||
| ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)`
|
||||
LL | if let inline!("abc") = "abc" {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"abc" == inline!("abc")`
|
||||
|
||||
error: aborting due to 14 previous errors
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// aux-build:proc_macro_derive.rs
|
||||
// aux-build:macro_rules.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![warn(clippy::field_reassign_with_default)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate proc_macro_derive;
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
extern crate proc_macros;
|
||||
use proc_macros::{external, inline_macros};
|
||||
|
||||
// Don't lint on derives that derive `Default`
|
||||
// See https://github.com/rust-lang/rust-clippy/issues/6545
|
||||
|
@ -36,14 +36,6 @@ struct D {
|
|||
b: Option<i32>,
|
||||
}
|
||||
|
||||
macro_rules! m {
|
||||
($key:ident: $value:tt) => {{
|
||||
let mut data = $crate::D::default();
|
||||
data.$key = Some($value);
|
||||
data
|
||||
}};
|
||||
}
|
||||
|
||||
/// Implements .next() that returns a different number each time.
|
||||
struct SideEffect(i32);
|
||||
|
||||
|
@ -57,6 +49,7 @@ impl SideEffect {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline_macros]
|
||||
fn main() {
|
||||
// wrong, produces first error in stderr
|
||||
let mut a: A = Default::default();
|
||||
|
@ -150,7 +143,18 @@ fn main() {
|
|||
a.i = vec![1];
|
||||
|
||||
// Don't lint in external macros
|
||||
field_reassign_with_default!();
|
||||
external! {
|
||||
#[derive(Default)]
|
||||
struct A {
|
||||
pub i: i32,
|
||||
pub j: i64,
|
||||
}
|
||||
fn lint() {
|
||||
let mut a: A = Default::default();
|
||||
a.i = 42;
|
||||
a;
|
||||
}
|
||||
}
|
||||
|
||||
// be sure suggestion is correct with generics
|
||||
let mut a: Wrapper<bool> = Default::default();
|
||||
|
@ -160,9 +164,11 @@ fn main() {
|
|||
a.i = 42;
|
||||
|
||||
// Don't lint in macros
|
||||
m! {
|
||||
a: 42
|
||||
};
|
||||
inline!(
|
||||
let mut data = $crate::D::default();
|
||||
data.$a = Some($42);
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
mod m {
|
||||
|
|
|
@ -1,132 +1,132 @@
|
|||
error: field assignment outside of initializer for an instance created with Default::default()
|
||||
--> $DIR/field_reassign_with_default.rs:63:5
|
||||
--> $DIR/field_reassign_with_default.rs:56:5
|
||||
|
|
||||
LL | a.i = 42;
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
|
||||
--> $DIR/field_reassign_with_default.rs:62:5
|
||||
--> $DIR/field_reassign_with_default.rs:55:5
|
||||
|
|
||||
LL | let mut a: A = Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: `-D clippy::field-reassign-with-default` implied by `-D warnings`
|
||||
|
||||
error: field assignment outside of initializer for an instance created with Default::default()
|
||||
--> $DIR/field_reassign_with_default.rs:103:5
|
||||
--> $DIR/field_reassign_with_default.rs:96:5
|
||||
|
|
||||
LL | a.j = 43;
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments
|
||||
--> $DIR/field_reassign_with_default.rs:102:5
|
||||
--> $DIR/field_reassign_with_default.rs:95:5
|
||||
|
|
||||
LL | let mut a: A = Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: field assignment outside of initializer for an instance created with Default::default()
|
||||
--> $DIR/field_reassign_with_default.rs:108:5
|
||||
--> $DIR/field_reassign_with_default.rs:101:5
|
||||
|
|
||||
LL | a.i = 42;
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments
|
||||
--> $DIR/field_reassign_with_default.rs:107:5
|
||||
--> $DIR/field_reassign_with_default.rs:100:5
|
||||
|
|
||||
LL | let mut a: A = Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: field assignment outside of initializer for an instance created with Default::default()
|
||||
--> $DIR/field_reassign_with_default.rs:114:5
|
||||
--> $DIR/field_reassign_with_default.rs:107:5
|
||||
|
|
||||
LL | a.i = 42;
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
|
||||
--> $DIR/field_reassign_with_default.rs:113:5
|
||||
--> $DIR/field_reassign_with_default.rs:106:5
|
||||
|
|
||||
LL | let mut a = A::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: field assignment outside of initializer for an instance created with Default::default()
|
||||
--> $DIR/field_reassign_with_default.rs:124:5
|
||||
--> $DIR/field_reassign_with_default.rs:117:5
|
||||
|
|
||||
LL | a.i = Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments
|
||||
--> $DIR/field_reassign_with_default.rs:123:5
|
||||
--> $DIR/field_reassign_with_default.rs:116:5
|
||||
|
|
||||
LL | let mut a: A = Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: field assignment outside of initializer for an instance created with Default::default()
|
||||
--> $DIR/field_reassign_with_default.rs:128:5
|
||||
--> $DIR/field_reassign_with_default.rs:121:5
|
||||
|
|
||||
LL | a.i = Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments
|
||||
--> $DIR/field_reassign_with_default.rs:127:5
|
||||
--> $DIR/field_reassign_with_default.rs:120:5
|
||||
|
|
||||
LL | let mut a: A = Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: field assignment outside of initializer for an instance created with Default::default()
|
||||
--> $DIR/field_reassign_with_default.rs:150:5
|
||||
--> $DIR/field_reassign_with_default.rs:143:5
|
||||
|
|
||||
LL | a.i = vec![1];
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments
|
||||
--> $DIR/field_reassign_with_default.rs:149:5
|
||||
--> $DIR/field_reassign_with_default.rs:142:5
|
||||
|
|
||||
LL | let mut a: C = C::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: field assignment outside of initializer for an instance created with Default::default()
|
||||
--> $DIR/field_reassign_with_default.rs:157:5
|
||||
--> $DIR/field_reassign_with_default.rs:161:5
|
||||
|
|
||||
LL | a.i = true;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
note: consider initializing the variable with `Wrapper::<bool> { i: true }` and removing relevant reassignments
|
||||
--> $DIR/field_reassign_with_default.rs:156:5
|
||||
--> $DIR/field_reassign_with_default.rs:160:5
|
||||
|
|
||||
LL | let mut a: Wrapper<bool> = Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: field assignment outside of initializer for an instance created with Default::default()
|
||||
--> $DIR/field_reassign_with_default.rs:160:5
|
||||
--> $DIR/field_reassign_with_default.rs:164:5
|
||||
|
|
||||
LL | a.i = 42;
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
note: consider initializing the variable with `WrapperMulti::<i32, i64> { i: 42, ..Default::default() }` and removing relevant reassignments
|
||||
--> $DIR/field_reassign_with_default.rs:159:5
|
||||
--> $DIR/field_reassign_with_default.rs:163:5
|
||||
|
|
||||
LL | let mut a: WrapperMulti<i32, i64> = Default::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: field assignment outside of initializer for an instance created with Default::default()
|
||||
--> $DIR/field_reassign_with_default.rs:229:13
|
||||
--> $DIR/field_reassign_with_default.rs:235:13
|
||||
|
|
||||
LL | f.name = name.len();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: consider initializing the variable with `issue6312::ImplDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments
|
||||
--> $DIR/field_reassign_with_default.rs:228:13
|
||||
--> $DIR/field_reassign_with_default.rs:234:13
|
||||
|
|
||||
LL | let mut f = ImplDropAllCopy::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: field assignment outside of initializer for an instance created with Default::default()
|
||||
--> $DIR/field_reassign_with_default.rs:245:13
|
||||
--> $DIR/field_reassign_with_default.rs:251:13
|
||||
|
|
||||
LL | f.name = name.len();
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: consider initializing the variable with `issue6312::NoDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments
|
||||
--> $DIR/field_reassign_with_default.rs:244:13
|
||||
--> $DIR/field_reassign_with_default.rs:250:13
|
||||
|
|
||||
LL | let mut f = NoDropAllCopy::default();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -43,4 +43,30 @@ fn ifs_same_cond() {
|
|||
}
|
||||
}
|
||||
|
||||
fn issue10272() {
|
||||
let a = String::from("ha");
|
||||
if a.contains("ah") {
|
||||
} else if a.contains("ah") {
|
||||
// Trigger this lint
|
||||
} else if a.contains("ha") {
|
||||
} else if a == "wow" {
|
||||
}
|
||||
|
||||
let p: *mut i8 = std::ptr::null_mut();
|
||||
if p.is_null() {
|
||||
} else if p.align_offset(0) == 0 {
|
||||
} else if p.is_null() {
|
||||
// ok, p is mutable pointer
|
||||
} else {
|
||||
}
|
||||
|
||||
let x = std::cell::Cell::new(true);
|
||||
if x.get() {
|
||||
} else if !x.take() {
|
||||
} else if x.get() {
|
||||
// ok, x is interior mutable type
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -35,5 +35,17 @@ note: same as this
|
|||
LL | if 2 * a == 1 {
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: this `if` has the same condition as a previous `if`
|
||||
--> $DIR/ifs_same_cond.rs:49:15
|
||||
|
|
||||
LL | } else if a.contains("ah") {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: same as this
|
||||
--> $DIR/ifs_same_cond.rs:48:8
|
||||
|
|
||||
LL | if a.contains("ah") {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
// aux-build:implicit_hasher_macros.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![deny(clippy::implicit_hasher)]
|
||||
#![allow(unused)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate implicit_hasher_macros;
|
||||
extern crate proc_macros;
|
||||
use proc_macros::external;
|
||||
|
||||
use std::cmp::Eq;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
@ -68,22 +70,19 @@ impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> {
|
|||
|
||||
pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
|
||||
|
||||
macro_rules! gen {
|
||||
(impl) => {
|
||||
#[proc_macros::inline_macros]
|
||||
pub mod gen {
|
||||
use super::*;
|
||||
inline! {
|
||||
impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
|
||||
fn make() -> (Self, Self) {
|
||||
(HashMap::new(), HashMap::with_capacity(10))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(fn $name:ident) => {
|
||||
pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
|
||||
};
|
||||
pub fn bar(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
|
||||
}
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
gen!(impl);
|
||||
gen!(fn bar);
|
||||
|
||||
// When the macro is in a different file, the suggestion spans can't be combined properly
|
||||
// and should not cause an ICE
|
||||
|
@ -94,7 +93,9 @@ pub mod test_macro;
|
|||
__implicit_hasher_test_macro!(impl<K, V> for HashMap<K, V> where V: test_macro::A);
|
||||
|
||||
// #4260
|
||||
implicit_hasher_fn!();
|
||||
external! {
|
||||
pub fn f(input: &HashMap<u32, u32>) {}
|
||||
}
|
||||
|
||||
// #7712
|
||||
pub async fn election_vote(_data: HashMap<i32, i32>) {}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
error: impl for `HashMap` should be generalized over different hashers
|
||||
--> $DIR/implicit_hasher.rs:16:35
|
||||
--> $DIR/implicit_hasher.rs:18:35
|
||||
|
|
||||
LL | impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> {
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/implicit_hasher.rs:2:9
|
||||
--> $DIR/implicit_hasher.rs:3:9
|
||||
|
|
||||
LL | #![deny(clippy::implicit_hasher)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -19,7 +19,7 @@ LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default:
|
|||
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: impl for `HashMap` should be generalized over different hashers
|
||||
--> $DIR/implicit_hasher.rs:25:36
|
||||
--> $DIR/implicit_hasher.rs:27:36
|
||||
|
|
||||
LL | impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
|
||||
| ^^^^^^^^^^^^^
|
||||
|
@ -34,7 +34,7 @@ LL | ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Defa
|
|||
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: impl for `HashMap` should be generalized over different hashers
|
||||
--> $DIR/implicit_hasher.rs:30:19
|
||||
--> $DIR/implicit_hasher.rs:32:19
|
||||
|
|
||||
LL | impl Foo<i16> for HashMap<String, String> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -49,7 +49,7 @@ LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default:
|
|||
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: impl for `HashSet` should be generalized over different hashers
|
||||
--> $DIR/implicit_hasher.rs:47:32
|
||||
--> $DIR/implicit_hasher.rs:49:32
|
||||
|
|
||||
LL | impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
|
||||
| ^^^^^^^^^^
|
||||
|
@ -64,7 +64,7 @@ LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default:
|
|||
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: impl for `HashSet` should be generalized over different hashers
|
||||
--> $DIR/implicit_hasher.rs:52:19
|
||||
--> $DIR/implicit_hasher.rs:54:19
|
||||
|
|
||||
LL | impl Foo<i16> for HashSet<String> {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
@ -79,7 +79,7 @@ LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default:
|
|||
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: parameter of type `HashMap` should be generalized over different hashers
|
||||
--> $DIR/implicit_hasher.rs:69:23
|
||||
--> $DIR/implicit_hasher.rs:71:23
|
||||
|
|
||||
LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
@ -90,7 +90,7 @@ LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _s
|
|||
| +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: parameter of type `HashSet` should be generalized over different hashers
|
||||
--> $DIR/implicit_hasher.rs:69:53
|
||||
--> $DIR/implicit_hasher.rs:71:53
|
||||
|
|
||||
LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
|
||||
| ^^^^^^^^^^^^
|
||||
|
@ -101,15 +101,12 @@ LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set:
|
|||
| +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
|
||||
|
||||
error: impl for `HashMap` should be generalized over different hashers
|
||||
--> $DIR/implicit_hasher.rs:73:43
|
||||
--> $DIR/implicit_hasher.rs:77:43
|
||||
|
|
||||
LL | impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
|
||||
| ^^^^^^^^^^^^^
|
||||
...
|
||||
LL | gen!(impl);
|
||||
| ---------- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `__inline_mac_mod_gen` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: consider adding a type parameter
|
||||
|
|
||||
LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> {
|
||||
|
@ -120,37 +117,31 @@ LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10,
|
|||
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: parameter of type `HashMap` should be generalized over different hashers
|
||||
--> $DIR/implicit_hasher.rs:81:33
|
||||
--> $DIR/implicit_hasher.rs:83:31
|
||||
|
|
||||
LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | gen!(fn bar);
|
||||
| ------------ in this macro invocation
|
||||
LL | pub fn bar(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `__inline_mac_mod_gen` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: consider adding a type parameter
|
||||
|
|
||||
LL | pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {}
|
||||
| +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
|
||||
LL | pub fn bar<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {}
|
||||
| +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: parameter of type `HashSet` should be generalized over different hashers
|
||||
--> $DIR/implicit_hasher.rs:81:63
|
||||
--> $DIR/implicit_hasher.rs:83:61
|
||||
|
|
||||
LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
|
||||
| ^^^^^^^^^^^^
|
||||
...
|
||||
LL | gen!(fn bar);
|
||||
| ------------ in this macro invocation
|
||||
LL | pub fn bar(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the macro `__inline_mac_mod_gen` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: consider adding a type parameter
|
||||
|
|
||||
LL | pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
|
||||
| +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
|
||||
LL | pub fn bar<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
|
||||
| +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
|
||||
|
||||
error: parameter of type `HashMap` should be generalized over different hashers
|
||||
--> $DIR/implicit_hasher.rs:100:35
|
||||
--> $DIR/implicit_hasher.rs:101:35
|
||||
|
|
||||
LL | pub async fn election_vote(_data: HashMap<i32, i32>) {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
// run-rustfix
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![warn(clippy::inconsistent_struct_constructor)]
|
||||
#![allow(clippy::redundant_field_names)]
|
||||
#![allow(clippy::unnecessary_operation)]
|
||||
#![allow(clippy::no_effect)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
extern crate proc_macros;
|
||||
|
||||
#[derive(Default)]
|
||||
struct Foo {
|
||||
x: i32,
|
||||
|
@ -12,18 +16,10 @@ struct Foo {
|
|||
z: i32,
|
||||
}
|
||||
|
||||
macro_rules! new_foo {
|
||||
() => {
|
||||
let x = 1;
|
||||
let y = 1;
|
||||
let z = 1;
|
||||
Foo { y, x, z }
|
||||
};
|
||||
}
|
||||
|
||||
mod without_base {
|
||||
use super::Foo;
|
||||
|
||||
#[proc_macros::inline_macros]
|
||||
fn test() {
|
||||
let x = 1;
|
||||
let y = 1;
|
||||
|
@ -34,7 +30,12 @@ mod without_base {
|
|||
|
||||
// Should NOT lint.
|
||||
// issue #7069.
|
||||
new_foo!();
|
||||
inline!({
|
||||
let x = 1;
|
||||
let y = 1;
|
||||
let z = 1;
|
||||
Foo { y, x, z }
|
||||
});
|
||||
|
||||
// Should NOT lint because the order is the same as in the definition.
|
||||
Foo { x, y, z };
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
// run-rustfix
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![warn(clippy::inconsistent_struct_constructor)]
|
||||
#![allow(clippy::redundant_field_names)]
|
||||
#![allow(clippy::unnecessary_operation)]
|
||||
#![allow(clippy::no_effect)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
extern crate proc_macros;
|
||||
|
||||
#[derive(Default)]
|
||||
struct Foo {
|
||||
x: i32,
|
||||
|
@ -12,18 +16,10 @@ struct Foo {
|
|||
z: i32,
|
||||
}
|
||||
|
||||
macro_rules! new_foo {
|
||||
() => {
|
||||
let x = 1;
|
||||
let y = 1;
|
||||
let z = 1;
|
||||
Foo { y, x, z }
|
||||
};
|
||||
}
|
||||
|
||||
mod without_base {
|
||||
use super::Foo;
|
||||
|
||||
#[proc_macros::inline_macros]
|
||||
fn test() {
|
||||
let x = 1;
|
||||
let y = 1;
|
||||
|
@ -34,7 +30,12 @@ mod without_base {
|
|||
|
||||
// Should NOT lint.
|
||||
// issue #7069.
|
||||
new_foo!();
|
||||
inline!({
|
||||
let x = 1;
|
||||
let y = 1;
|
||||
let z = 1;
|
||||
Foo { y, x, z }
|
||||
});
|
||||
|
||||
// Should NOT lint because the order is the same as in the definition.
|
||||
Foo { x, y, z };
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: struct constructor field order is inconsistent with struct definition field order
|
||||
--> $DIR/inconsistent_struct_constructor.rs:33:9
|
||||
--> $DIR/inconsistent_struct_constructor.rs:29:9
|
||||
|
|
||||
LL | Foo { y, x, z };
|
||||
| ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }`
|
||||
|
@ -7,7 +7,7 @@ LL | Foo { y, x, z };
|
|||
= note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings`
|
||||
|
||||
error: struct constructor field order is inconsistent with struct definition field order
|
||||
--> $DIR/inconsistent_struct_constructor.rs:55:9
|
||||
--> $DIR/inconsistent_struct_constructor.rs:56:9
|
||||
|
|
||||
LL | / Foo {
|
||||
LL | | z,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// aux-build:macro_rules.rs
|
||||
// aux-build:proc_macros.rs
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
#![warn(clippy::large_enum_variant)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
extern crate proc_macros;
|
||||
use proc_macros::external;
|
||||
|
||||
enum LargeEnum {
|
||||
A(i32),
|
||||
|
@ -155,5 +155,10 @@ enum LargeEnumOfConst {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
large_enum_variant!();
|
||||
external!(
|
||||
enum LargeEnumInMacro {
|
||||
A(i32),
|
||||
B([i32; 8000]),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ mod a {
|
|||
use mac;
|
||||
use mini_mac::ClippyMiniMacroTest;
|
||||
use mini_mac;
|
||||
use mac::{inner::foofoo, inner::try_err};
|
||||
use mac::{inner::mut_mut, inner::try_err};
|
||||
use mac::inner;
|
||||
use mac::inner::nested::string_add;
|
||||
use mac::inner::nested;
|
||||
|
@ -36,7 +36,7 @@ mod a {
|
|||
let v: ty_macro!() = Vec::default();
|
||||
|
||||
inner::try_err!();
|
||||
inner::foofoo!();
|
||||
inner::mut_mut!();
|
||||
nested::string_add!();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ mod a {
|
|||
let v: ty_macro!() = Vec::default();
|
||||
|
||||
inner::try_err!();
|
||||
inner::foofoo!();
|
||||
inner::mut_mut!();
|
||||
nested::string_add!();
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue