## Unreleased / In Rust Nightly
-[c2c07fa...master](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...master)
+[09bd400...master](https://github.com/rust-lang/rust-clippy/compare/09bd400...master)
+
+## Rust 1.47
+
+Current beta, release 2020-10-08
+
+[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
+
+### New lints
+
+* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848)
+* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852)
+* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694)
+* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737)
+* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841)
+* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773)
+* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825)
+* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869)
+* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769)
+* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809)
+* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750)
+* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301)
+
+### Moves and Deprecations
+
+* Deprecate [`regex_macro`] lint
+ [#5760](https://github.com/rust-lang/rust-clippy/pull/5760)
+* Move [`range_minus_one`] to `pedantic`
+ [#5752](https://github.com/rust-lang/rust-clippy/pull/5752)
+
+### Enhancements
+
+* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls
+ [#5837](https://github.com/rust-lang/rust-clippy/pull/5837)
+* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting
+ [#5811](https://github.com/rust-lang/rust-clippy/pull/5811)
+* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`]
+ [#5443](https://github.com/rust-lang/rust-clippy/pull/5443)
+* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`]
+ [#5701](https://github.com/rust-lang/rust-clippy/pull/5701)
+* Make it possible to allow [`unsafe_derive_deserialize`]
+ [#5870](https://github.com/rust-lang/rust-clippy/pull/5870)
+* Catch `ord.min(a).max(b)` where a < b in [`min_max`]
+ [#5871](https://github.com/rust-lang/rust-clippy/pull/5871)
+* Make [`clone_on_copy`] suggestion machine applicable
+ [#5745](https://github.com/rust-lang/rust-clippy/pull/5745)
+* Enable [`len_zero`] on ranges now that `is_empty` is stable on them
+ [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
+
+### False Positive Fixes
+
+* Avoid triggering [`or_fun_call`] with const fns that take no arguments
+ [#5889](https://github.com/rust-lang/rust-clippy/pull/5889)
+* Fix [`redundant_closure_call`] false positive for closures that have multiple calls
+ [#5800](https://github.com/rust-lang/rust-clippy/pull/5800)
+* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`]
+ [#5824](https://github.com/rust-lang/rust-clippy/pull/5824)
+* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`]
+ [#5771](https://github.com/rust-lang/rust-clippy/pull/5771)
+* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled
+ [#5758](https://github.com/rust-lang/rust-clippy/pull/5758)
+* Avoid linting if key borrows in [`unnecessary_sort_by`]
+ [#5756](https://github.com/rust-lang/rust-clippy/pull/5756)
+* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`]
+ [#5857](https://github.com/rust-lang/rust-clippy/pull/5857)
+* Take input lifetimes into account in `manual_async_fn`
+ [#5859](https://github.com/rust-lang/rust-clippy/pull/5859)
+* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option
+ [#5761](https://github.com/rust-lang/rust-clippy/pull/5761)
+* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation
+ [#5820](https://github.com/rust-lang/rust-clippy/pull/5820)
+
+### Suggestion Fixes/Improvements
+
+* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet
+ [#5788](https://github.com/rust-lang/rust-clippy/pull/5788)
+* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`]
+ [#5846](https://github.com/rust-lang/rust-clippy/pull/5846)
+* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`]
+ [#5793](https://github.com/rust-lang/rust-clippy/pull/5793)
+* Drop borrow operator in suggestions of [`redundant_pattern_matching`]
+ [#5815](https://github.com/rust-lang/rust-clippy/pull/5815)
+* Add suggestion for [`iter_skip_next`]
+ [#5843](https://github.com/rust-lang/rust-clippy/pull/5843)
+* Improve [`collapsible_if`] fix suggestion
+ [#5732](https://github.com/rust-lang/rust-clippy/pull/5732)
+
+### ICE Fixes
+
+* Fix ICE caused by [`needless_collect`]
+ [#5877](https://github.com/rust-lang/rust-clippy/pull/5877)
+* Fix ICE caused by [`unnested_or_patterns`]
+ [#5784](https://github.com/rust-lang/rust-clippy/pull/5784)
+
+### Documentation Improvements
+
+* Fix grammar of [`await_holding_lock`] documentation
+ [#5748](https://github.com/rust-lang/rust-clippy/pull/5748)
+
+### Others
+
+* Make lints adhere to the rustc dev guide
+ [#5888](https://github.com/rust-lang/rust-clippy/pull/5888)
## Rust 1.46
-Current beta, release 2020-08-27
+Current stable, released 2020-08-27
[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
## Rust 1.45
-Current stable, released 2020-07-16
+Released 2020-07-16
[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
+[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
--- /dev/null
+use crate::utils::{implements_trait, snippet, span_lint_and_then};
+use rustc_errors::Applicability;
+use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for async blocks that yield values of types
+ /// that can themselves be awaited.
+ ///
+ /// **Why is this bad?** An await is likely missing.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// async fn foo() {}
+ ///
+ /// fn bar() {
+ /// let x = async {
+ /// foo()
+ /// };
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// async fn foo() {}
+ ///
+ /// fn bar() {
+ /// let x = async {
+ /// foo().await
+ /// };
+ /// }
+ /// ```
+ pub ASYNC_YIELDS_ASYNC,
+ correctness,
+ "async blocks that return a type that can be awaited"
+}
+
+declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]);
+
+impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
+ fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
+ use AsyncGeneratorKind::{Block, Closure};
+ // For functions, with explicitly defined types, don't warn.
+ // XXXkhuey maybe we should?
+ if let Some(GeneratorKind::Async(Block | Closure)) = body.generator_kind {
+ if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() {
+ let body_id = BodyId {
+ hir_id: body.value.hir_id,
+ };
+ let def_id = cx.tcx.hir().body_owner_def_id(body_id);
+ let typeck_results = cx.tcx.typeck(def_id);
+ let expr_ty = typeck_results.expr_ty(&body.value);
+
+ if implements_trait(cx, expr_ty, future_trait_def_id, &[]) {
+ let return_expr_span = match &body.value.kind {
+ // XXXkhuey there has to be a better way.
+ ExprKind::Block(block, _) => block.expr.map(|e| e.span),
+ ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span),
+ _ => None,
+ };
+ if let Some(return_expr_span) = return_expr_span {
+ span_lint_and_then(
+ cx,
+ ASYNC_YIELDS_ASYNC,
+ return_expr_span,
+ "an async construct yields a type which is itself awaitable",
+ |db| {
+ db.span_label(body.value.span, "outer async construct");
+ db.span_label(return_expr_span, "awaitable value not awaited");
+ db.span_suggestion(
+ return_expr_span,
+ "consider awaiting this value",
+ format!("{}.await", snippet(cx, return_expr_span, "..")),
+ Applicability::MaybeIncorrect,
+ );
+ },
+ );
+ }
+ }
+ }
+ }
+ }
+}
];
fn type_is_atomic(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- if let ty::Adt(&ty::AdtDef { did, .. }, _) = cx.typeck_results().expr_ty(expr).kind {
+ if let ty::Adt(&ty::AdtDef { did, .. }, _) = cx.typeck_results().expr_ty(expr).kind() {
ATOMIC_TYPES
.iter()
.any(|ty| match_def_path(cx, did, &["core", "sync", "atomic", ty]))
/// **What it does:** Checks for `extern crate` and `use` items annotated with
/// lint attributes.
///
- /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]` and
- /// `#[allow(unreachable_pub)]` on `use` items and `#[allow(unused_imports)]` on
+ /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,
+ /// `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and
+ /// `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on
/// `extern crate` items with a `#[macro_use]` attribute.
///
/// **Why is this bad?** Lint attributes have no effect on crate imports. Most
if let Some(ident) = attr.ident() {
match &*ident.as_str() {
"allow" | "warn" | "deny" | "forbid" => {
- // permit `unused_imports`, `deprecated` and `unreachable_pub` for `use` items
+ // permit `unused_imports`, `deprecated`, `unreachable_pub`,
+ // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items
// and `unused_imports` for `extern crate` items with `macro_use`
for lint in lint_list {
match item.kind {
|| is_word(lint, sym!(deprecated))
|| is_word(lint, sym!(unreachable_pub))
|| is_word(lint, sym!(unused))
+ || extract_clippy_lint(lint)
+ .map_or(false, |s| s == "wildcard_imports")
+ || extract_clippy_lint(lint).map_or(false, |s| s == "enum_glob_use")
{
return;
}
}
}
-fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) {
- fn extract_name(lint: &NestedMetaItem) -> Option<SymbolStr> {
- if_chain! {
- if let Some(meta_item) = lint.meta_item();
- if meta_item.path.segments.len() > 1;
- if let tool_name = meta_item.path.segments[0].ident;
- if tool_name.as_str() == "clippy";
- let lint_name = meta_item.path.segments.last().unwrap().ident.name;
- then {
- return Some(lint_name.as_str());
- }
+/// Returns the lint name if it is clippy lint.
+fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<SymbolStr> {
+ if_chain! {
+ if let Some(meta_item) = lint.meta_item();
+ if meta_item.path.segments.len() > 1;
+ if let tool_name = meta_item.path.segments[0].ident;
+ if tool_name.as_str() == "clippy";
+ let lint_name = meta_item.path.segments.last().unwrap().ident.name;
+ then {
+ return Some(lint_name.as_str());
}
- None
}
+ None
+}
+fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) {
let lint_store = cx.lints();
for lint in items {
- if let Some(lint_name) = extract_name(lint) {
+ if let Some(lint_name) = extract_clippy_lint(lint) {
if let CheckLintNameResult::Tool(Err((None, _))) =
lint_store.check_lint_name(&lint_name, Some(sym!(clippy)))
{
fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
for ty_cause in ty_causes {
- if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind {
+ if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
if is_mutex_guard(cx, adt.did) {
span_lint_and_note(
cx,
_ => { return; }
}
};
- if ty::Uint(UintTy::U8) != walk_ptrs_ty(cx.typeck_results().expr_ty(needle)).kind {
+ if ty::Uint(UintTy::U8) != *walk_ptrs_ty(cx.typeck_results().expr_ty(needle)).kind() {
return;
}
let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) =
(&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
(&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
(&Self::Int(l), &Self::Int(r)) => {
- if let ty::Int(int_ty) = cmp_type.kind {
+ if let ty::Int(int_ty) = *cmp_type.kind() {
Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
} else {
Some(l.cmp(&r))
FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()),
},
- LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind {
+ LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() {
ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
_ => bug!(),
ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec),
ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple),
ExprKind::Repeat(ref value, _) => {
- let n = match self.typeck_results.expr_ty(e).kind {
+ let n = match self.typeck_results.expr_ty(e).kind() {
ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?,
_ => span_bug!(e.span, "typeck error"),
};
Bool(b) => Some(Bool(!b)),
Int(value) => {
let value = !value;
- match ty.kind {
+ match *ty.kind() {
ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
_ => None,
use self::Constant::{Int, F32, F64};
match *o {
Int(value) => {
- let ity = match ty.kind {
+ let ity = match *ty.kind() {
ty::Int(ity) => ity,
_ => return None,
};
let l = self.expr(left)?;
let r = self.expr(right);
match (l, r) {
- (Constant::Int(l), Some(Constant::Int(r))) => match self.typeck_results.expr_ty_opt(left)?.kind {
+ (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() {
ty::Int(ity) => {
let l = sext(self.lcx.tcx, l, ity);
let r = sext(self.lcx.tcx, r, ity);
use rustc_middle::mir::interpret::{ConstValue, Scalar};
match result.val {
ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data: d, .. })) => {
- match result.ty.kind {
+ match result.ty.kind() {
ty::Bool => Some(Constant::Bool(d == 1)),
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(d)),
ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
d.try_into().expect("invalid f64 bit representation"),
))),
ty::RawPtr(type_and_mut) => {
- if let ty::Uint(_) = type_and_mut.ty.kind {
+ if let ty::Uint(_) = type_and_mut.ty.kind() {
return Some(Constant::RawPtr(d));
}
None
_ => None,
}
},
- ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind {
- ty::Ref(_, tam, _) => match tam.kind {
+ ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() {
+ ty::Ref(_, tam, _) => match tam.kind() {
ty::Str => String::from_utf8(
data.inspect_with_uninit_and_ptr_outside_interpreter(start..end)
.to_owned(),
},
_ => None,
},
- ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind {
- ty::Array(sub_type, len) => match sub_type.kind {
+ ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() {
+ ty::Array(sub_type, len) => match sub_type.kind() {
ty::Float(FloatTy::F32) => match miri_to_const(len) {
Some(Constant::Int(len)) => alloc
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize))
if let ExprKind::Path(ref qpath) = path.kind;
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
+ // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
+ if let QPath::Resolved(None, _path) = qpath;
then {
- match qpath {
- QPath::Resolved(..) => {
- if_chain! {
- // Detect and ignore <Foo as Default>::default() because these calls do
- // explicitly name the type.
- if let ExprKind::Call(ref method, ref _args) = expr.kind;
- if let ExprKind::Path(ref p) = method.kind;
- if let QPath::Resolved(Some(_ty), _path) = p;
- then {
- return;
- }
- }
-
- // TODO: Work out a way to put "whatever the imported way of referencing
- // this type in this file" rather than a fully-qualified type.
- let expr_ty = cx.typeck_results().expr_ty(expr);
- if let ty::Adt(..) = expr_ty.kind {
- let replacement = format!("{}::default()", expr_ty);
- span_lint_and_sugg(
- cx,
- DEFAULT_TRAIT_ACCESS,
- expr.span,
- &format!("calling `{}` is more clear than this expression", replacement),
- "try",
- replacement,
- Applicability::Unspecified, // First resolve the TODO above
- );
- }
- },
- QPath::TypeRelative(..) | QPath::LangItem(..) => {},
+ let expr_ty = cx.typeck_results().expr_ty(expr);
+ if let ty::Adt(def, ..) = expr_ty.kind() {
+ // TODO: Work out a way to put "whatever the imported way of referencing
+ // this type in this file" rather than a fully-qualified type.
+ let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
+ span_lint_and_sugg(
+ cx,
+ DEFAULT_TRAIT_ACCESS,
+ expr.span,
+ &format!("calling `{}` is more clear than this expression", replacement),
+ "try",
+ replacement,
+ Applicability::Unspecified, // First resolve the TODO above
+ );
}
}
}
return;
}
- match ty.kind {
+ match *ty.kind() {
ty::Adt(def, _) if def.is_union() => return,
// Some types are not Clone by default but could be cloned “by hand” if necessary
ty::Adt(def, substs) => {
for variant in &def.variants {
for field in &variant.fields {
- if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind {
+ if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind() {
return;
}
}
for subst in substs {
if let ty::subst::GenericArgKind::Type(subst) = subst.unpack() {
- if let ty::Param(_) = subst.kind {
+ if let ty::Param(_) = subst.kind() {
return;
}
}
if_chain! {
if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE);
- if let ty::Adt(def, _) = ty.kind;
+ if let ty::Adt(def, _) = ty.kind();
if let Some(local_def_id) = def.did.as_local();
let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
if !is_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
let mir = cx.tcx.optimized_mir(def_id.to_def_id());
let ret_ty = mir.return_ty();
if implements_trait(cx, ret_ty, future, &[]);
- if let ty::Opaque(_, subs) = ret_ty.kind;
+ if let ty::Opaque(_, subs) = ret_ty.kind();
if let Some(gen) = subs.types().next();
- if let ty::Generator(_, subs, _) = gen.kind;
+ if let ty::Generator(_, subs, _) = gen.kind();
if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym!(result_type));
then {
span_lint(
let arg = &args[0];
let arg_ty = cx.typeck_results().expr_ty(arg);
- if let ty::Ref(..) = arg_ty.kind {
+ if let ty::Ref(..) = arg_ty.kind() {
if match_def_path(cx, def_id, &paths::DROP) {
lint = DROP_REF;
msg = DROP_REF_SUMMARY.to_string();
.ok()
.map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty));
if let Some(Constant::Int(val)) = constant.and_then(miri_to_const) {
- if let ty::Adt(adt, _) = ty.kind {
+ if let ty::Adt(adt, _) = ty.kind() {
if adt.is_enum() {
ty = adt.repr.discr_type().to_ty(cx.tcx);
}
}
- match ty.kind {
+ match ty.kind() {
ty::Int(IntTy::Isize) => {
let val = ((val as i128) << 64) >> 64;
if i32::try_from(val).is_ok() {
let fn_ty = cx.typeck_results().expr_ty(caller);
- if matches!(fn_ty.kind, ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _));
+ if matches!(fn_ty.kind(), ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _));
if !type_is_unsafe_function(cx, fn_ty);
}
fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
- match (&lhs.kind, &rhs.kind) {
+ match (&lhs.kind(), &rhs.kind()) {
(ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2),
(l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))),
}
}
fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
- match (&lhs.kind, &rhs.kind) {
+ match (&lhs.kind(), &rhs.kind()) {
(ty::Bool, ty::Bool)
| (ty::Char, ty::Char)
| (ty::Int(_), ty::Int(_))
}
fn get_type_name(cx: &LateContext<'_>, ty: Ty<'_>) -> String {
- match ty.kind {
+ match ty.kind() {
ty::Adt(t, _) => cx.tcx.def_path_str(t.did),
ty::Ref(_, r, _) => get_type_name(cx, &r),
_ => ty.to_string(),
ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e),
ExprKind::Call(ref func, _) => {
let typ = self.cx.typeck_results().expr_ty(func);
- match typ.kind {
+ match typ.kind() {
ty::FnDef(..) | ty::FnPtr(_) => {
let sig = typ.fn_sig(self.cx.tcx);
- if let ty::Never = self.cx.tcx.erase_late_bound_regions(&sig).output().kind {
+ if let ty::Never = self.cx.tcx.erase_late_bound_regions(&sig).output().kind() {
self.report_diverging_sub_expr(e);
}
},
// values of the substractions on the left hand side are of the type float
let t_val_l = cx.typeck_results().expr_ty(val_l);
let t_val_r = cx.typeck_results().expr_ty(val_r);
- if let ty::Float(_) = t_val_l.kind;
- if let ty::Float(_) = t_val_r.kind;
+ if let ty::Float(_) = t_val_l.kind();
+ if let ty::Float(_) = t_val_r.kind();
then {
let sug_l = sugg::Sugg::hir(cx, &val_l, "..");
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if_chain! {
let ty = cx.typeck_results().expr_ty(expr);
- if let ty::Float(fty) = ty.kind;
+ if let ty::Float(fty) = *ty.kind();
if let hir::ExprKind::Lit(ref lit) = expr.kind;
if let LitKind::Float(sym, lit_float_ty) = lit.node;
then {
if_chain! {
// if the expression is a float literal and it is unsuffixed then
// add a suffix so the suggestion is valid and unambiguous
- if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind;
+ if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind();
if let ExprKind::Lit(lit) = &expr.kind;
if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node;
then {
if pats.len() == 1;
then {
let ty = walk_ptrs_ty(cx.typeck_results().pat_ty(&pats[0]));
- if ty.kind != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) {
+ if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) {
return None;
}
if let ExprKind::Lit(ref lit) = format_args.kind {
}
if line_count > self.max_lines {
- span_lint(cx, TOO_MANY_LINES, span, "this function has a large number of lines")
+ span_lint(
+ cx,
+ TOO_MANY_LINES,
+ span,
+ &format!("this function has too many lines ({}/{})", line_count, self.max_lines),
+ )
}
}
static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]];
fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet<DefId>) -> bool {
- match ty.kind {
+ match *ty.kind() {
// primitive types are never mutable
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
ty::Adt(ref adt, ref substs) => {
return;
}
let ret_ty = utils::return_ty(cx, hir_id);
- if let Opaque(id, subst) = ret_ty.kind {
+ if let Opaque(id, subst) = *ret_ty.kind() {
let preds = cx.tcx.predicates_of(id).instantiate(cx.tcx, subst);
let mut is_future = false;
for p in preds.predicates {
#[allow(clippy::cast_possible_wrap)]
fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e) {
- let check = match cx.typeck_results().expr_ty(e).kind {
+ let check = match *cx.typeck_results().expr_ty(e).kind() {
ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
ty::Uint(uty) => clip(cx.tcx, !0, uty),
_ => return,
let ty = cx.typeck_results().expr_ty(array);
if let Some(range) = higher::range(index) {
// Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
- if let ty::Array(_, s) = ty.kind {
+ if let ty::Array(_, s) = ty.kind() {
let size: u128 = if let Some(size) = s.try_eval_usize(cx.tcx, cx.param_env) {
size.into()
} else {
span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic.", None, help_msg);
} else {
// Catchall non-range index, i.e., [n] or [n << m]
- if let ty::Array(..) = ty.kind {
+ if let ty::Array(..) = ty.kind() {
// Index is a constant uint.
if let Some(..) = constant(cx, cx.typeck_results(), index) {
// Let rustc's `const_err` lint handle constant `usize` indexing on arrays.
if !item.span.from_expansion();
if let ItemKind::Const(hir_ty, _) = &item.kind;
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
- if let ty::Array(element_type, cst) = ty.kind;
+ if let ty::Array(element_type, cst) = ty.kind();
if let ConstKind::Value(val) = cst.val;
if let ConstValue::Scalar(element_count) = val;
if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if let ExprKind::Repeat(_, _) = expr.kind;
- if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind;
+ if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind();
if let ConstKind::Value(val) = cst.val;
if let ConstValue::Scalar(element_count) = val;
if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
}
let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr));
- match ty.kind {
+ match ty.kind() {
ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| {
cx.tcx
.associated_items(principal.def_id())
mod as_conversions;
mod assertions_on_constants;
mod assign_ops;
+mod async_yields_async;
mod atomic_ordering;
mod attrs;
mod await_holding_lock;
&assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
&assign_ops::ASSIGN_OP_PATTERN,
&assign_ops::MISREFACTORED_ASSIGN_OP,
+ &async_yields_async::ASYNC_YIELDS_ASYNC,
&atomic_ordering::INVALID_ATOMIC_ORDERING,
&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
&attrs::DEPRECATED_CFG_ATTR,
store.register_late_pass(|| box unwrap_in_result::UnwrapInResult);
store.register_late_pass(|| box self_assignment::SelfAssignment);
store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
+ store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP),
+ LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC),
LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING),
LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
LintId::of(&attrs::DEPRECATED_CFG_ATTR),
store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
LintId::of(&approx_const::APPROX_CONSTANT),
+ LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC),
LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING),
LintId::of(&attrs::DEPRECATED_SEMVER),
LintId::of(&attrs::MISMATCHED_TARGET_OS),
}
fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool {
- let is_slice = match ty.kind {
+ let is_slice = match ty.kind() {
ty::Ref(_, subty, _) => is_slice_like(cx, subty),
ty::Slice(..) | ty::Array(..) => true,
_ => false,
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, "");
+
+ span_lint_and_help(
+ cx,
+ SAME_ITEM_PUSH,
+ vec.span,
+ "it looks like the same item is being pushed into this Vec",
+ None,
+ &format!(
+ "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
+ item_str, vec_str, item_str
+ ),
+ )
+ }
+
+ if !matches!(pat.kind, PatKind::Wild) {
+ return;
+ }
+
// Determine whether it is safe to lint the body
let mut same_item_push_visitor = SameItemPushVisitor {
should_lint: true,
walk_expr(&mut same_item_push_visitor, body);
if same_item_push_visitor.should_lint {
if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push {
- // Make sure that the push does not involve possibly mutating values
- if let PatKind::Wild = pat.kind {
- let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
- let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
- if let ExprKind::Path(ref qpath) = pushed_item.kind {
- if_chain! {
- if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id);
- let node = cx.tcx.hir().get(hir_id);
- if let Node::Binding(pat) = node;
- if let PatKind::Binding(bind_ann, ..) = pat.kind;
- if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
- then {
- span_lint_and_help(
- cx,
- SAME_ITEM_PUSH,
- vec.span,
- "it looks like the same item is being pushed into this Vec",
- None,
- &format!(
- "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
- item_str, vec_str, item_str
- ),
- )
+ let vec_ty = cx.typeck_results().expr_ty(vec);
+ let ty = vec_ty.walk().nth(1).unwrap().expect_ty();
+ if cx
+ .tcx
+ .lang_items()
+ .clone_trait()
+ .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+ {
+ // Make sure that the push does not involve possibly mutating values
+ match pushed_item.kind {
+ ExprKind::Path(ref qpath) => {
+ match qpath_res(cx, qpath, pushed_item.hir_id) {
+ // immutable bindings that are initialized with literal or constant
+ Res::Local(hir_id) => {
+ if_chain! {
+ let node = cx.tcx.hir().get(hir_id);
+ if let Node::Binding(pat) = node;
+ if let PatKind::Binding(bind_ann, ..) = pat.kind;
+ if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
+ let parent_node = cx.tcx.hir().get_parent_node(hir_id);
+ if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node);
+ if let Some(init) = parent_let_expr.init;
+ then {
+ match init.kind {
+ // immutable bindings that are initialized with literal
+ ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
+ // immutable bindings that are initialized with constant
+ ExprKind::Path(ref path) => {
+ if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) {
+ emit_lint(cx, vec, pushed_item);
+ }
+ }
+ _ => {},
+ }
+ }
+ }
+ },
+ // constant
+ Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item),
+ _ => {},
}
- }
- } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) {
- span_lint_and_help(
- cx,
- SAME_ITEM_PUSH,
- vec.span,
- "it looks like the same item is being pushed into this Vec",
- None,
- &format!(
- "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
- item_str, vec_str, item_str
- ),
- )
+ },
+ ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
+ _ => {},
}
}
}
if_chain! {
if let ExprKind::Lit(ref lit) = end.kind;
if let ast::LitKind::Int(end_int, _) = lit.node;
- if let ty::Array(_, arr_len_const) = indexed_ty.kind;
+ if let ty::Array(_, arr_len_const) = indexed_ty.kind();
if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env);
then {
return match limits {
if let PatKind::Tuple(ref pat, _) = pat.kind {
if pat.len() == 2 {
let arg_span = arg.span;
- let (new_pat_span, kind, ty, mutbl) = match cx.typeck_results().expr_ty(arg).kind {
+ let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() {
ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) {
(key, _) if pat_is_wild(key, body) => (pat[1].span, "value", ty, mutbl),
(_, value) if pat_is_wild(value, body) => (pat[0].span, "key", ty, Mutability::Not),
for expr in args {
let ty = self.cx.typeck_results().expr_ty_adjusted(expr);
self.prefer_mutable = false;
- if let ty::Ref(_, _, mutbl) = ty.kind {
+ if let ty::Ref(_, _, mutbl) = *ty.kind() {
if mutbl == Mutability::Mut {
self.prefer_mutable = true;
}
let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
for (ty, expr) in self.cx.tcx.fn_sig(def_id).inputs().skip_binder().iter().zip(args) {
self.prefer_mutable = false;
- if let ty::Ref(_, _, mutbl) = ty.kind {
+ if let ty::Ref(_, _, mutbl) = *ty.kind() {
if mutbl == Mutability::Mut {
self.prefer_mutable = true;
}
fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
// IntoIterator is currently only implemented for array sizes <= 32 in rustc
- match ty.kind {
+ match ty.kind() {
ty::Array(_, n) => n
.try_eval_usize(cx.tcx, cx.param_env)
.map_or(false, |val| (0..=32).contains(&val)),
match closure_expr.kind {
hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner) => {
if ident_eq(name, inner) {
- if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind {
+ if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
lint(cx, e.span, args[0].span, true);
}
}
&& match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) {
let obj_ty = cx.typeck_results().expr_ty(&obj[0]);
- if let ty::Ref(_, ty, _) = obj_ty.kind {
+ if let ty::Ref(_, ty, _) = obj_ty.kind() {
let copy = is_copy(cx, ty);
lint(cx, e.span, args[0].span, copy);
} else {
declare_clippy_lint! {
/// **What it does:** Checks for usage of `option.map(f)` where f is a function
- /// or closure that returns the unit type.
+ /// or closure that returns the unit type `()`.
///
/// **Why is this bad?** Readability, this can be written more clearly with
/// an if let statement
declare_clippy_lint! {
/// **What it does:** Checks for usage of `result.map(f)` where f is a function
- /// or closure that returns the unit type.
+ /// or closure that returns the unit type `()`.
///
/// **Why is this bad?** Readability, this can be written more clearly with
/// an if let statement
declare_lint_pass!(MapUnit => [OPTION_MAP_UNIT_FN, RESULT_MAP_UNIT_FN]);
fn is_unit_type(ty: Ty<'_>) -> bool {
- match ty.kind {
+ match ty.kind() {
ty::Tuple(slice) => slice.is_empty(),
ty::Never => true,
_ => false,
fn is_unit_function(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
let ty = cx.typeck_results().expr_ty(expr);
- if let ty::FnDef(id, _) = ty.kind {
+ if let ty::FnDef(id, _) = *ty.kind() {
if let Some(fn_type) = cx.tcx.fn_sig(id).no_bound_vars() {
return is_unit_type(fn_type.output());
}
#[must_use]
fn suggestion_msg(function_type: &str, map_type: &str) -> String {
format!(
- "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type",
+ "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type `()`",
map_type, function_type
)
}
if let QPath::Resolved(_, ref path) = qpath;
if let Some(def_id) = path.res.opt_def_id();
let ty = cx.tcx.type_of(def_id);
- if let ty::Adt(def, _) = ty.kind;
+ if let ty::Adt(def, _) = ty.kind();
if def.is_struct() || def.is_union();
if fields.len() == def.non_enum_variant().fields.len();
};
let ty = cx.typeck_results().expr_ty(ex);
- if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) {
+ if *ty.kind() != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) {
check_single_match_single_pattern(cx, ex, arms, expr, els);
check_single_match_opt_like(cx, ex, arms, expr, ty, els);
}
fn check_match_bool(cx: &LateContext<'_>, ex: &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(ex).kind() == ty::Bool {
span_lint_and_then(
cx,
MATCH_BOOL,
// already covered.
let mut missing_variants = vec![];
- if let ty::Adt(def, _) = ty.kind {
+ if let ty::Adt(def, _) = ty.kind() {
for variant in &def.variants {
missing_variants.push(variant);
}
let mut message = "wildcard match will miss any future added variants";
- if let ty::Adt(def, _) = ty.kind {
+ if let ty::Adt(def, _) = ty.kind() {
if def.is_variant_list_non_exhaustive() {
message = "match on non-exhaustive enum doesn't explicitly match all known variants";
suggestion.push(String::from("_"));
let input_ty = cx.typeck_results().expr_ty(ex);
let cast = if_chain! {
- if let ty::Adt(_, substs) = input_ty.kind;
+ if let ty::Adt(_, substs) = input_ty.kind();
let input_ty = substs.type_at(0);
- if let ty::Adt(_, substs) = output_ty.kind;
+ if let ty::Adt(_, substs) = output_ty.kind();
let output_ty = substs.type_at(0);
- if let ty::Ref(_, output_ty, _) = output_ty.kind;
+ if let ty::Ref(_, output_ty, _) = *output_ty.kind();
if input_ty != output_ty;
then {
".map(|x| x as _)"
/// Returns whether `ty` specializes `ToString`.
/// Currently, these are `str`, `String`, and `Cow<'_, str>`.
fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
- if let ty::Str = ty.kind {
+ if let ty::Str = ty.kind() {
return true;
}
return true;
}
- if let ty::Adt(adt, substs) = ty.kind {
+ if let ty::Adt(adt, substs) = ty.kind() {
match_def_path(cx, adt.did, &paths::COW) && substs.type_at(1).is_str()
} else {
false
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::{TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
use rustc_middle::hir::map::Map;
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::{self, Ty, TyS};
+use rustc_middle::ty::{self, TraitRef, Ty, TyS};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::symbol::{sym, SymbolStr};
use crate::consts::{constant, Constant};
use crate::utils::usage::mutated_variables;
use crate::utils::{
- get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy,
- is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
- match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths,
- remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite,
- span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty,
- walk_ptrs_ty_depth, SpanlessEq,
+ contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro,
+ is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats,
+ last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls,
+ method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability,
+ snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
+ span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
};
declare_clippy_lint! {
/// **Known problems:** None.
///
/// **Example:**
+ /// In an impl block:
/// ```rust
/// # struct Foo;
/// # struct NotAFoo;
///
/// ```rust
/// # struct Foo;
- /// # struct FooError;
+ /// struct Bar(Foo);
/// impl Foo {
- /// // Good. Return type contains `Self`
- /// fn new() -> Result<Foo, FooError> {
- /// # Ok(Foo)
+ /// // Bad. The type name must contain `Self`
+ /// fn new() -> Bar {
+ /// # Bar(Foo)
/// }
/// }
/// ```
///
/// ```rust
/// # struct Foo;
- /// struct Bar(Foo);
+ /// # struct FooError;
/// impl Foo {
- /// // Bad. The type name must contain `Self`.
- /// fn new() -> Bar {
- /// # Bar(Foo)
+ /// // Good. Return type contains `Self`
+ /// fn new() -> Result<Foo, FooError> {
+ /// # Ok(Foo)
/// }
/// }
/// ```
+ ///
+ /// Or in a trait definition:
+ /// ```rust
+ /// pub trait Trait {
+ /// // Bad. The type name must contain `Self`
+ /// fn new();
+ /// }
+ /// ```
+ ///
+ /// ```rust
+ /// pub trait Trait {
+ /// // Good. Return type contains `Self`
+ /// fn new() -> Self;
+ /// }
+ /// ```
pub NEW_RET_NO_SELF,
style,
"not returning type containing `Self` in a `new` method"
}
declare_clippy_lint! {
- /// **What it does:** Warns when using push_str with a single-character string literal,
- /// and push with a char would work fine.
+ /// **What it does:** Warns when using `push_str` with a single-character string literal,
+ /// and `push` with a `char` would work fine.
///
- /// **Why is this bad?** It's less clear that we are pushing a single character
+ /// **Why is this bad?** It's less clear that we are pushing a single character.
///
/// **Known problems:** None
///
/// **Example:**
- /// ```
+ /// ```rust
/// let mut string = String::new();
/// string.push_str("R");
/// ```
/// Could be written as
- /// ```
+ /// ```rust
/// let mut string = String::new();
/// string.push('R');
/// ```
}
}
- match self_ty.kind {
- ty::Ref(_, ty, _) if ty.kind == ty::Str => {
+ match self_ty.kind() {
+ ty::Ref(_, ty, _) if *ty.kind() == ty::Str => {
for &(method, pos) in &PATTERN_METHODS {
if method_call.ident.name.as_str() == method && args.len() > pos {
lint_single_char_pattern(cx, expr, &args[pos]);
}
}
+ // if this impl block implements a trait, lint in trait definition instead
+ if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
+ return;
+ }
+
if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
let ret_ty = return_ty(cx, impl_item.hir_id);
- let contains_self_ty = |ty: Ty<'tcx>| {
- ty.walk().any(|inner| match inner.unpack() {
- GenericArgKind::Type(inner_ty) => TyS::same_type(self_ty, inner_ty),
-
- GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
- })
- };
-
// walk the return type and check for Self (this does not check associated types)
- if contains_self_ty(ret_ty) {
+ if contains_ty(ret_ty, self_ty) {
return;
}
// if return type is impl trait, check the associated types
- if let ty::Opaque(def_id, _) = ret_ty.kind {
+ if let ty::Opaque(def_id, _) = *ret_ty.kind() {
// one of the associated types must be Self
for &(predicate, _span) in cx.tcx.predicates_of(def_id).predicates {
if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() {
// walk the associated type and check for Self
- if contains_self_ty(projection_predicate.ty) {
+ if contains_ty(projection_predicate.ty, self_ty) {
return;
}
}
}
}
}
+
+ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
+ if_chain! {
+ if !in_external_macro(cx.tcx.sess, item.span);
+ if item.ident.name == sym!(new);
+ if let TraitItemKind::Fn(_, _) = item.kind;
+ let ret_ty = return_ty(cx, item.hir_id);
+ let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty();
+ if !contains_ty(ret_ty, self_ty);
+
+ then {
+ span_lint(
+ cx,
+ NEW_RET_NO_SELF,
+ item.span,
+ "methods called `new` usually return `Self`",
+ );
+ }
+ }
+ }
}
/// Checks for the `OR_FUN_CALL` lint.
if path.ident.as_str() == "len" {
let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0]));
- match ty.kind {
+ match ty.kind() {
ty::Slice(_) | ty::Array(_, _) => return,
_ => (),
}
&& {
let arg_type = cx.typeck_results().expr_ty(&call_args[0]);
let base_type = walk_ptrs_ty(arg_type);
- base_type.kind == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type))
+ *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type))
}
{
&call_args[0]
if is_type_diagnostic_item(cx, arg_ty, sym!(string_type)) {
return false;
}
- if let ty::Ref(_, ty, ..) = arg_ty.kind {
- if ty.kind == ty::Str && can_be_static_str(cx, arg) {
+ if let ty::Ref(_, ty, ..) = arg_ty.kind() {
+ if *ty.kind() == ty::Str && can_be_static_str(cx, arg) {
return false;
}
};
if let hir::ExprKind::Path(ref p) = fun.kind {
match cx.qpath_res(p, fun.hir_id) {
hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!(
- cx.tcx.fn_sig(def_id).output().skip_binder().kind,
+ cx.tcx.fn_sig(def_id).output().skip_binder().kind(),
ty::Ref(ty::ReStatic, ..)
),
_ => false,
.type_dependent_def_id(arg.hir_id)
.map_or(false, |method_id| {
matches!(
- cx.tcx.fn_sig(method_id).output().skip_binder().kind,
+ cx.tcx.fn_sig(method_id).output().skip_binder().kind(),
ty::Ref(ty::ReStatic, ..)
)
})
/// Checks for the `CLONE_ON_COPY` lint.
fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) {
let ty = cx.typeck_results().expr_ty(expr);
- if let ty::Ref(_, inner, _) = arg_ty.kind {
- if let ty::Ref(_, innermost, _) = inner.kind {
+ if let ty::Ref(_, inner, _) = arg_ty.kind() {
+ if let ty::Ref(_, innermost, _) = inner.kind() {
span_lint_and_then(
cx,
CLONE_DOUBLE_REF,
if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
let mut ty = innermost;
let mut n = 0;
- while let ty::Ref(_, inner, _) = ty.kind {
+ while let ty::Ref(_, inner, _) = ty.kind() {
ty = inner;
n += 1;
}
fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(arg));
- if let ty::Adt(_, subst) = obj_ty.kind {
+ if let ty::Adt(_, subst) = obj_ty.kind() {
let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) {
"Rc"
} else if is_type_diagnostic_item(cx, obj_ty, sym::Arc) {
if let Some(arglists) = method_chain_args(arg, &["chars"]) {
let target = &arglists[0][0];
let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(target));
- let ref_str = if self_ty.kind == ty::Str {
+ let ref_str = if *self_ty.kind() == ty::Str {
""
} else if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) {
"&"
fn lint_cstring_as_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, source: &hir::Expr<'_>, unwrap: &hir::Expr<'_>) {
if_chain! {
let source_type = cx.typeck_results().expr_ty(source);
- if let ty::Adt(def, substs) = source_type.kind;
+ if let ty::Adt(def, substs) = source_type.kind();
if cx.tcx.is_diagnostic_item(sym!(result_type), def.did);
if match_type(cx, substs.type_at(0), &paths::CSTRING);
then {
}
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym!(vec_type))
|| matches!(
- &walk_ptrs_ty(cx.typeck_results().expr_ty(caller_expr)).kind,
+ &walk_ptrs_ty(cx.typeck_results().expr_ty(caller_expr)).kind(),
ty::Array(_, _)
)
{
ty: Ty<'tcx>,
) -> Option<&'tcx hir::Expr<'tcx>> {
fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
- match ty.kind {
+ match ty.kind() {
ty::Slice(_) => true,
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)),
None
}
} else {
- match ty.kind {
+ match ty.kind() {
ty::Slice(_) => Some(expr),
ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
ty::Ref(_, inner, _) => {
// lint if caller of `.map().flatten()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]);
- let is_map_to_option = match map_closure_ty.kind {
+ let is_map_to_option = match map_closure_ty.kind() {
ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
- let map_closure_sig = match map_closure_ty.kind {
+ let map_closure_sig = match map_closure_ty.kind() {
ty::Closure(_, substs) => substs.as_closure().sig(),
_ => map_closure_ty.fn_sig(cx.tcx),
};
let mut applicability = Applicability::MachineApplicable;
let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty_adjusted(&args[0][0]));
- if self_ty.kind != ty::Str {
+ if *self_ty.kind() != ty::Str {
return false;
}
fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(&'static str, &'static str)> {
has_iter_method(cx, self_ref_ty).map(|ty_name| {
- let mutbl = match self_ref_ty.kind {
+ let mutbl = match self_ref_ty.kind() {
ty::Ref(_, _, mutbl) => mutbl,
_ => unreachable!(),
};
}
fn is_maybe_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
- match ty.kind {
+ match ty.kind() {
ty::Array(ref component, _) => is_maybe_uninit_ty_valid(cx, component),
ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)),
ty::Adt(ref adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT),
/// Given a `Result<T, E>` type, return its error type (`E`).
fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
- match ty.kind {
+ match ty.kind() {
ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym!(result_type)) => substs.types().nth(1),
_ => None,
}
} else if ty.is_box() {
ty.boxed_ty() == parent_ty
} else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
- if let ty::Adt(_, substs) = ty.kind {
+ if let ty::Adt(_, substs) = ty.kind() {
substs.types().next().map_or(false, |t| t == parent_ty)
} else {
false
}
fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
- if let ty::Ref(_, t, m) = ty.kind {
+ if let ty::Ref(_, t, m) = *ty.kind() {
return m == mutability && t == parent_ty;
}
fn check_pointer_offset(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
if_chain! {
if args.len() == 2;
- if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(&args[0]).kind;
+ if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(&args[0]).kind();
if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty));
if layout.is_zst();
then {
}
fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- let value = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind;
+ let value = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind();
if let ty::Array(arr_ty, _) = value {
- return matches!(arr_ty.kind, ty::Float(_));
+ return matches!(arr_ty.kind(), ty::Float(_));
};
matches!(value, ty::Float(_))
}
fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- matches!(&walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind, ty::Array(_, _))
+ matches!(&walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind(), ty::Array(_, _))
}
fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OperandInfo> {
match constant(cx, cx.typeck_results(), operand) {
- Some((Constant::Int(v), _)) => match cx.typeck_results().expr_ty(expr).kind {
+ Some((Constant::Int(v), _)) => match *cx.typeck_results().expr_ty(expr).kind() {
ty::Int(ity) => {
let value = sext(cx.tcx, v, ity);
return Some(OperandInfo {
// generics (because the compiler cannot ensure immutability for unknown types).
fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
let ty = walk_ptrs_ty(ty);
- if let Adt(def, substs) = ty.kind {
+ if let Adt(def, substs) = ty.kind() {
if [&paths::HASHMAP, &paths::BTREEMAP, &paths::HASHSET, &paths::BTREESET]
.iter()
.any(|path| match_def_path(cx, def.did, &**path))
}
fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
- match ty.kind {
+ match *ty.kind() {
RawPtr(TypeAndMut { ty: inner_ty, mutbl }) | Ref(_, inner_ty, mutbl) => {
mutbl == hir::Mutability::Mut || is_mutable_type(cx, inner_ty, span)
},
expr.span,
"generally you want to avoid `&mut &mut _` if possible",
);
- } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind {
+ } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() {
span_lint(
self.cx,
MUT_MUT,
name: &str,
fn_kind: &str,
) {
- match type_definition.kind {
+ match type_definition.kind() {
ty::FnDef(..) | ty::FnPtr(_) => {
let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
for (argument, parameter) in arguments.iter().zip(parameters.iter()) {
- match parameter.kind {
+ match parameter.kind() {
ty::Ref(_, _, Mutability::Not)
| ty::RawPtr(ty::TypeAndMut {
mutbl: Mutability::Not, ..
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
if adj
.iter()
- .any(|a| matches!(a.target.kind, ty::Ref(_, _, Mutability::Mut)))
+ .any(|a| matches!(a.target.kind(), ty::Ref(_, _, Mutability::Mut)))
{
self.found = true;
return;
impl<'tcx> LateLintPass<'tcx> for Mutex {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let ty = cx.typeck_results().expr_ty(expr);
- if let ty::Adt(_, subst) = ty.kind {
+ if let ty::Adt(_, subst) = ty.kind() {
if is_type_diagnostic_item(cx, ty, sym!(mutex_type)) {
let mutex_param = subst.type_at(0);
if let Some(atomic_name) = get_atomic_name(mutex_param) {
behavior and not the internal type, consider using `Mutex<()>`",
atomic_name
);
- match mutex_param.kind {
+ match *mutex_param.kind() {
ty::Uint(t) if t != ast::UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg),
ty::Int(t) if t != ast::IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg),
_ => span_lint(cx, MUTEX_ATOMIC, expr.span, &msg),
}
fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> {
- match ty.kind {
+ match ty.kind() {
ty::Bool => Some("AtomicBool"),
ty::Uint(_) => Some("AtomicUsize"),
ty::Int(_) => Some("AtomicIsize"),
return;
}
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind {
- if let ty::Ref(..) = cx.typeck_results().expr_ty(inner).kind {
+ if let ty::Ref(..) = cx.typeck_results().expr_ty(inner).kind() {
for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) {
if let [Adjustment {
kind: Adjust::Deref(_), ..
}
if_chain! {
if let PatKind::Binding(BindingAnnotation::Ref, .., name, _) = pat.kind;
- if let ty::Ref(_, tam, mutbl) = cx.typeck_results().pat_ty(pat).kind;
+ if let ty::Ref(_, tam, mutbl) = *cx.typeck_results().pat_ty(pat).kind();
if mutbl == Mutability::Not;
- if let ty::Ref(_, _, mutbl) = tam.kind;
+ if let ty::Ref(_, _, mutbl) = *tam.kind();
// only lint immutable refs, because borrowed `&mut T` cannot be moved out
if mutbl == Mutability::Not;
then {
// Dereference suggestion
let sugg = |diag: &mut DiagnosticBuilder<'_>| {
- if let ty::Adt(def, ..) = ty.kind {
+ if let ty::Adt(def, ..) = ty.kind() {
if let Some(span) = cx.tcx.hir().span_if_local(def.did) {
if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() {
diag.span_help(span, "consider marking this type as `Copy`");
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind {
let ty = cx.typeck_results().expr_ty(expr);
- if let ty::Adt(def, _) = ty.kind {
+ if let ty::Adt(def, _) = ty.kind() {
if fields.len() == def.non_enum_variant().fields.len() {
span_lint(
cx,
diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)");
},
Source::Assoc { ty: ty_span, .. } => {
- if ty.flags.intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) {
+ if ty.flags().intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) {
diag.span_label(ty_span, &format!("consider requiring `{}` to be `Copy`", ty));
}
},
level: Level,
) -> Option<(Span, Mutability, Level)> {
if let PatKind::Ref(ref sub_pat, _) = pat.kind {
- if let TyKind::Ref(_, sub_ty, _) = ty.kind {
+ if let TyKind::Ref(_, sub_ty, _) = ty.kind() {
return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower);
}
}
- if let TyKind::Ref(_, _, mutability) = ty.kind {
+ if let TyKind::Ref(_, _, mutability) = *ty.kind() {
if is_non_ref_pattern(&pat.kind) {
return Some((pat.span, mutability, level));
}
}
if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind {
- if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
+ if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind() {
if let Some(variant) = get_variant(adt_def, qpath) {
let field_defs = &variant.fields;
return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref);
}
if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind {
- if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
+ if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind() {
if let Some(variant) = get_variant(adt_def, qpath) {
let field_defs = &variant.fields;
let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref));
}
if let PatKind::Tuple(ref pats, _) = pat.kind {
- if let TyKind::Tuple(..) = ty.kind {
+ if let TyKind::Tuple(..) = ty.kind() {
return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields());
}
}
}
}
- if let ty::Ref(_, ty, Mutability::Not) = ty.kind {
+ if let ty::Ref(_, ty, Mutability::Not) = ty.kind() {
if is_type_diagnostic_item(cx, ty, sym!(vec_type)) {
let mut ty_snippet = None;
if_chain! {
if_chain! {
if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(expr);
let ty = cx.typeck_results().expr_ty(start);
- if let ty::Int(_) | ty::Uint(_) = ty.kind;
+ if let ty::Int(_) | ty::Uint(_) = ty.kind();
if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start);
if let Some((end_idx, _)) = constant(cx, cx.typeck_results(), end);
if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx);
visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
};
use rustc_middle::ty::{self, fold::TypeVisitor, Ty};
-use rustc_mir::dataflow::BottomValue;
use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{BytePos, Span};
continue;
}
- if let ty::Adt(ref def, _) = arg_ty.kind {
+ if let ty::Adt(ref def, _) = arg_ty.kind() {
if match_def_path(cx, def.did, &paths::MEM_MANUALLY_DROP) {
continue;
}
if let mir::TerminatorKind::Call { func, args, destination, .. } = kind;
if args.len() == 1;
if let mir::Operand::Move(mir::Place { local, .. }) = &args[0];
- if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).kind;
+ if let ty::FnDef(def_id, _) = *func.ty(&*mir, cx.tcx).kind();
if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
if !is_copy(cx, inner_ty);
then {
struct MaybeStorageLive;
impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive {
- type Idx = mir::Local;
+ type Domain = BitSet<mir::Local>;
const NAME: &'static str = "maybe_storage_live";
- fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
- body.local_decls.len()
+ fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
+ // bottom = dead
+ BitSet::new_empty(body.local_decls.len())
}
- fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
+ fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
for arg in body.args_iter() {
state.insert(arg);
}
}
impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive {
+ type Idx = mir::Local;
+
fn statement_effect(&self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, _: mir::Location) {
match stmt.kind {
mir::StatementKind::StorageLive(l) => trans.gen(l),
}
}
-impl BottomValue for MaybeStorageLive {
- /// bottom = dead
- const BOTTOM_VALUE: bool = false;
-}
-
/// Collects the possible borrowers of each local.
/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
/// possible borrowers of `a`.
fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool {
let var_ty = cx.typeck_results().node_type_opt(pat_id);
- var_ty.map_or(false, |var_ty| !matches!(var_ty.kind, ty::Adt(..)))
+ var_ty.map_or(false, |var_ty| !matches!(var_ty.kind(), ty::Adt(..)))
}
fn check_pat<'tcx>(
if eq_expr_value(cx, lhs1, lhs2) {
let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(lhs1));
- if matches!(ty.kind, ty::Slice(_))
- || matches!(ty.kind, ty::Array(_, _))
+ if matches!(ty.kind(), ty::Slice(_))
+ || matches!(ty.kind(), ty::Array(_, _))
|| is_type_diagnostic_item(cx, ty, sym!(vec_type))
|| is_type_diagnostic_item(cx, ty, sym!(vecdeque_type))
{
use crate::utils::{is_adjusted, span_lint};
-use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
"assignments to temporaries"
}
-fn is_temporary(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- match &expr.kind {
- ExprKind::Struct(..) | ExprKind::Tup(..) => true,
- ExprKind::Path(qpath) => matches!(cx.qpath_res(qpath, expr.hir_id), Res::Def(DefKind::Const, ..)),
- _ => false,
- }
+fn is_temporary(expr: &Expr<'_>) -> bool {
+ matches!(&expr.kind, ExprKind::Struct(..) | ExprKind::Tup(..))
}
declare_lint_pass!(TemporaryAssignment => [TEMPORARY_ASSIGNMENT]);
while let ExprKind::Field(f, _) | ExprKind::Index(f, _) = &base.kind {
base = f;
}
- if is_temporary(cx, base) && !is_adjusted(cx, base) {
+ if is_temporary(base) && !is_adjusted(cx, base) {
span_lint(cx, TEMPORARY_ASSIGNMENT, expr.span, "assignment to temporary");
}
}
if let [char_arg, radix_arg] = &**to_digit_args;
if to_digits_path.ident.name.as_str() == "to_digit";
let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg);
- if char_arg_ty.kind == ty::Char;
+ if *char_arg_ty.kind() == ty::Char;
then {
Some((true, char_arg, radix_arg))
} else {
if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::TRANSMUTE);
then {
- // Avoid suggesting from/to bits in const contexts.
+ // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts.
// See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`.
+ // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers.
let const_context = in_constant(cx, e.hir_id);
let from_ty = cx.typeck_results().expr_ty(&args[0]);
let to_ty = cx.typeck_results().expr_ty(e);
- match (&from_ty.kind, &to_ty.kind) {
+ match (&from_ty.kind(), &to_ty.kind()) {
_ if from_ty == to_ty => span_lint(
cx,
USELESS_TRANSMUTE,
&format!("transmute from a `{}` to a `char`", from_ty),
|diag| {
let arg = sugg::Sugg::hir(cx, &args[0], "..");
- let arg = if let ty::Int(_) = from_ty.kind {
+ let arg = if let ty::Int(_) = from_ty.kind() {
arg.as_ty(ast::UintTy::U32.name_str())
} else {
arg
},
(ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) => {
if_chain! {
- if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind, &ty_to.kind);
- if let ty::Uint(ast::UintTy::U8) = slice_ty.kind;
+ if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind(), &ty_to.kind());
+ if let ty::Uint(ast::UintTy::U8) = slice_ty.kind();
if from_mutbl == to_mutbl;
then {
let postfix = if *from_mutbl == Mutability::Mut {
Applicability::Unspecified,
);
} else {
- if cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty) {
+ if (cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty))
+ && !const_context {
span_lint_and_then(
cx,
TRANSMUTE_PTR_TO_PTR,
&format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
|diag| {
let arg = sugg::Sugg::hir(cx, &args[0], "..");
- let arg = if let ty::Int(int_ty) = from_ty.kind {
+ let arg = if let ty::Int(int_ty) = from_ty.kind() {
arg.as_ty(format!(
"u{}",
int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string())
arg = sugg::Sugg::NonParen(format!("{}.to_bits()", arg.maybe_par()).into());
// cast the result of `to_bits` if `to_ty` is signed
- arg = if let ty::Int(int_ty) = to_ty.kind {
+ arg = if let ty::Int(int_ty) = to_ty.kind() {
arg.as_ty(int_ty.name_str().to_string())
} else {
arg
// Use lifetimes to determine if we're returning a reference to the
// argument. In that case we can't switch to pass-by-value as the
// argument will not live long enough.
- let output_lts = match fn_sig.output().kind {
+ let output_lts = match *fn_sig.output().kind() {
ty::Ref(output_lt, _, _) => vec![output_lt],
ty::Adt(_, substs) => substs.regions().collect(),
_ => vec![],
}
if_chain! {
- if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind;
+ if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind();
if !output_lts.contains(&input_lt);
if is_copy(cx, ty);
if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
/// Extracts the error type from Result<T, E>.
fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
if_chain! {
- if let ty::Adt(_, subst) = ty.kind;
+ if let ty::Adt(_, subst) = ty.kind();
if is_type_diagnostic_item(cx, ty, sym!(result_type));
let err_ty = subst.type_at(1);
then {
/// Extracts the error type from Poll<Result<T, E>>.
fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
if_chain! {
- if let ty::Adt(def, subst) = ty.kind;
+ if let ty::Adt(def, subst) = ty.kind();
if match_def_path(cx, def.did, &paths::POLL);
let ready_ty = subst.type_at(0);
- if let ty::Adt(ready_def, ready_subst) = ready_ty.kind;
+ if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
if cx.tcx.is_diagnostic_item(sym!(result_type), ready_def.did);
let err_ty = ready_subst.type_at(1);
/// Extracts the error type from Poll<Option<Result<T, E>>>.
fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
if_chain! {
- if let ty::Adt(def, subst) = ty.kind;
+ if let ty::Adt(def, subst) = ty.kind();
if match_def_path(cx, def.did, &paths::POLL);
let ready_ty = subst.type_at(0);
- if let ty::Adt(ready_def, ready_subst) = ready_ty.kind;
+ if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
if cx.tcx.is_diagnostic_item(sym!(option_type), ready_def.did);
let some_ty = ready_subst.type_at(0);
- if let ty::Adt(some_def, some_subst) = some_ty.kind;
+ if let ty::Adt(some_def, some_subst) = some_ty.kind();
if cx.tcx.is_diagnostic_item(sym!(result_type), some_def.did);
let err_ty = some_subst.type_at(1);
use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::{
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
- ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn,
- TraitItem, TraitItemKind, TyKind, UnOp,
+ ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind,
+ TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map;
use crate::utils::{
clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item,
last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral,
- qpath_res, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability,
- snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext,
+ qpath_res, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite,
+ span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext,
};
declare_clippy_lint! {
/// **What it does:** Checks for use of `Box<Vec<_>>` anywhere in the code.
+ /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
///
/// **Why is this bad?** `Vec` already keeps its contents in a separate area on
/// the heap. So if you `Box` it, you just add another level of indirection
declare_clippy_lint! {
/// **What it does:** Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.
+ /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
///
/// **Why is this bad?** `Vec` already keeps its contents in a separate area on
/// the heap. So if you `Box` its contents, you just add another level of indirection.
declare_clippy_lint! {
/// **What it does:** Checks for use of `&Box<T>` anywhere in the code.
+ /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
///
/// **Why is this bad?** Any `&Box<T>` can also be a `&T`, which is more
/// general.
}
}
+fn fmt_stmts_and_call(
+ cx: &LateContext<'_>,
+ call_expr: &Expr<'_>,
+ call_snippet: &str,
+ args_snippets: &[impl AsRef<str>],
+ non_empty_block_args_snippets: &[impl AsRef<str>],
+) -> String {
+ let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0);
+ let call_snippet_with_replacements = args_snippets
+ .iter()
+ .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1));
+
+ let mut stmts_and_call = non_empty_block_args_snippets
+ .iter()
+ .map(|it| it.as_ref().to_owned())
+ .collect::<Vec<_>>();
+ stmts_and_call.push(call_snippet_with_replacements);
+ stmts_and_call = stmts_and_call
+ .into_iter()
+ .map(|v| reindent_multiline(v.into(), true, Some(call_expr_indent)).into_owned())
+ .collect();
+
+ let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent)));
+ // expr is not in a block statement or result expression position, wrap in a block
+ let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id));
+ if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) {
+ let block_indent = call_expr_indent + 4;
+ stmts_and_call_snippet =
+ reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned();
+ stmts_and_call_snippet = format!(
+ "{{\n{}{}\n{}}}",
+ " ".repeat(block_indent),
+ &stmts_and_call_snippet,
+ " ".repeat(call_expr_indent)
+ );
+ }
+ stmts_and_call_snippet
+}
+
fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) {
let mut applicability = Applicability::MachineApplicable;
let (singular, plural) = if args_to_recover.len() > 1 {
Applicability::MaybeIncorrect,
);
or = "or ";
+ applicability = Applicability::MaybeIncorrect;
});
- let sugg = args_to_recover
+
+ let arg_snippets: Vec<String> = args_to_recover
+ .iter()
+ .filter_map(|arg| snippet_opt(cx, arg.span))
+ .collect();
+ let arg_snippets_without_empty_blocks: Vec<String> = args_to_recover
.iter()
.filter(|arg| !is_empty_block(arg))
- .enumerate()
- .map(|(i, arg)| {
- let indent = if i == 0 {
- 0
- } else {
- indent_of(cx, expr.span).unwrap_or(0)
- };
- format!(
- "{}{};",
- " ".repeat(indent),
- snippet_block_with_applicability(cx, arg.span, "..", Some(expr.span), &mut applicability)
- )
- })
- .collect::<Vec<String>>();
- let mut and = "";
- if !sugg.is_empty() {
- let plural = if sugg.len() > 1 { "s" } else { "" };
- db.span_suggestion(
- expr.span.with_hi(expr.span.lo()),
- &format!("{}move the expression{} in front of the call...", or, plural),
- format!("{}\n", sugg.join("\n")),
- applicability,
+ .filter_map(|arg| snippet_opt(cx, arg.span))
+ .collect();
+
+ if let Some(call_snippet) = snippet_opt(cx, expr.span) {
+ let sugg = fmt_stmts_and_call(
+ cx,
+ expr,
+ &call_snippet,
+ &arg_snippets,
+ &arg_snippets_without_empty_blocks,
);
- and = "...and "
+
+ if arg_snippets_without_empty_blocks.is_empty() {
+ db.multipart_suggestion(
+ &format!("use {}unit literal{} instead", singular, plural),
+ args_to_recover
+ .iter()
+ .map(|arg| (arg.span, "()".to_string()))
+ .collect::<Vec<_>>(),
+ applicability,
+ );
+ } else {
+ let plural = arg_snippets_without_empty_blocks.len() > 1;
+ let empty_or_s = if plural { "s" } else { "" };
+ let it_or_them = if plural { "them" } else { "it" };
+ db.span_suggestion(
+ expr.span,
+ &format!(
+ "{}move the expression{} in front of the call and replace {} with the unit literal `()`",
+ or, empty_or_s, it_or_them
+ ),
+ sugg,
+ applicability,
+ );
+ }
}
- db.multipart_suggestion(
- &format!("{}use {}unit literal{} instead", and, singular, plural),
- args_to_recover
- .iter()
- .map(|arg| (arg.span, "()".to_string()))
- .collect::<Vec<_>>(),
- applicability,
- );
},
);
}
}
fn is_unit(ty: Ty<'_>) -> bool {
- matches!(ty.kind, ty::Tuple(slice) if slice.is_empty())
+ matches!(ty.kind(), ty::Tuple(slice) if slice.is_empty())
}
fn is_unit_literal(expr: &Expr<'_>) -> bool {
/// Returns the size in bits of an integral type.
/// Will return 0 if the type is not an int or uint variant
fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 {
- match typ.kind {
+ match typ.kind() {
ty::Int(i) => match i {
IntTy::Isize => tcx.data_layout.pointer_size.bits(),
IntTy::I8 => 8,
}
fn is_isize_or_usize(typ: Ty<'_>) -> bool {
- matches!(typ.kind, ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
+ matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
}
fn span_precision_loss_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) {
if_chain! {
if let Some((const_val, _)) = const_val;
if let Constant::Int(n) = const_val;
- if let ty::Int(ity) = cast_from.kind;
+ if let ty::Int(ity) = *cast_from.kind();
if sext(cx.tcx, n, ity) >= 0;
then {
return
// Check if the given type is either `core::ffi::c_void` or
// one of the platform specific `libc::<platform>::c_void` of libc.
fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
- if let ty::Adt(adt, _) = ty.kind {
+ if let ty::Adt(adt, _) = ty.kind() {
let names = cx.get_def_path(adt.did);
if names.is_empty() {
/// Returns the mantissa bits wide of a fp type.
/// Will return 0 if the type is not a fp
fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
- match typ.kind {
+ match typ.kind() {
ty::Float(FloatTy::F32) => 23,
ty::Float(FloatTy::F64) | ty::Infer(InferTy::FloatVar(_)) => 52,
_ => 0,
match lit.node {
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {},
_ => {
- if cast_from.kind == cast_to.kind && !in_external_macro(cx.sess(), expr.span) {
+ if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
span_lint(
cx,
UNNECESSARY_CAST,
match (cast_from.is_integral(), cast_to.is_integral()) {
(true, false) => {
let from_nbits = int_ty_to_nbits(cast_from, cx.tcx);
- let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind {
+ let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
32
} else {
64
check_lossless(cx, expr, cast_expr, cast_from, cast_to);
},
(false, false) => {
- if let (&ty::Float(FloatTy::F64), &ty::Float(FloatTy::F32)) = (&cast_from.kind, &cast_to.kind) {
+ if let (&ty::Float(FloatTy::F64), &ty::Float(FloatTy::F32)) = (&cast_from.kind(), &cast_to.kind()) {
span_lint(
cx,
CAST_POSSIBLE_TRUNCATION,
"casting `f64` to `f32` may truncate the value",
);
}
- if let (&ty::Float(FloatTy::F32), &ty::Float(FloatTy::F64)) = (&cast_from.kind, &cast_to.kind) {
+ if let (&ty::Float(FloatTy::F32), &ty::Float(FloatTy::F64)) = (&cast_from.kind(), &cast_to.kind()) {
span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to);
}
},
fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) {
if_chain! {
- if let ty::RawPtr(from_ptr_ty) = &cast_from.kind;
- if let ty::RawPtr(to_ptr_ty) = &cast_to.kind;
+ if let ty::RawPtr(from_ptr_ty) = &cast_from.kind();
+ if let ty::RawPtr(to_ptr_ty) = &cast_to.kind();
if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty);
if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty);
if from_layout.align.abi < to_layout.align.abi;
cast_to: Ty<'_>,
) {
// We only want to check casts to `ty::Uint` or `ty::Int`
- match cast_to.kind {
+ match cast_to.kind() {
ty::Uint(_) | ty::Int(..) => { /* continue on */ },
_ => return,
}
- match cast_from.kind {
+ match cast_from.kind() {
ty::FnDef(..) | ty::FnPtr(_) => {
let mut applicability = Applicability::MaybeIncorrect;
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
format!("{} as usize", from_snippet),
applicability,
);
- } else if cast_to.kind != ty::Uint(UintTy::Usize) {
+ } else if *cast_to.kind() != ty::Uint(UintTy::Usize) {
span_lint_and_sugg(
cx,
FN_TO_NUMERIC_CAST,
if let ExprKind::Cast(e, _) = &expr.kind;
if let ExprKind::Lit(l) = &e.kind;
if let LitKind::Char(c) = l.node;
- if ty::Uint(UintTy::U8) == cx.typeck_results().expr_ty(expr).kind;
+ if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind();
then {
let mut applicability = Applicability::MachineApplicable;
let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability);
let cv = constant(cx, cx.typeck_results(), expr)?.0;
- let which = match (&ty.kind, cv) {
+ let which = match (ty.kind(), cv) {
(&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => Minimum,
(&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => {
Minimum
})
}
}
+
impl Ord for FullInt {
#[must_use]
fn cmp(&self, other: &Self) -> Ordering {
if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) {
return None;
}
- match pre_cast_ty.kind {
+ match pre_cast_ty.kind() {
ty::Int(int_ty) => Some(match int_ty {
IntTy::I8 => (FullInt::S(i128::from(i8::MIN)), FullInt::S(i128::from(i8::MAX))),
IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))),
fn node_as_const_fullint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<FullInt> {
let val = constant(cx, cx.typeck_results(), expr)?.0;
if let Constant::Int(const_int) = val {
- match cx.typeck_results().expr_ty(expr).kind {
+ match *cx.typeck_results().expr_ty(expr).kind() {
ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))),
ty::Uint(_) => Some(FullInt::U(const_int)),
_ => None,
if let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind;
if let ExprKind::Cast(e, t) = &e.kind;
if let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind;
- if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind;
+ if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind();
then {
span_lint(
cx,
fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Span, Option<Span>)> {
if_chain! {
if let ExprKind::Closure(_, _fn_decl, body_id, span, _) = arg.kind;
- if let ty::Closure(_def_id, substs) = &cx.typeck_results().node_type(arg.hir_id).kind;
+ if let ty::Closure(_def_id, substs) = &cx.typeck_results().node_type(arg.hir_id).kind();
let ret_ty = substs.as_closure().sig().output();
let ty = cx.tcx.erase_late_bound_regions(&ret_ty);
if ty.is_unit();
}
fn is_trait_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- match cx.typeck_results().expr_ty_adjusted(expr).kind {
+ match cx.typeck_results().expr_ty_adjusted(expr).kind() {
ty::RawPtr(ty::TypeAndMut { ty, .. }) => ty.is_trait(),
_ => false,
}
}
fn is_fn_def(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- matches!(cx.typeck_results().expr_ty(expr).kind, ty::FnDef(..))
+ matches!(cx.typeck_results().expr_ty(expr).kind(), ty::FnDef(..))
}
if_chain! {
use crate::utils;
-use crate::utils::paths;
use crate::utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_errors::Applicability;
}
fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
+ // NOTE: Vectors of references are not supported. In order to avoid hitting https://github.com/rust-lang/rust/issues/34162,
+ // (different unnamed lifetimes for closure arg and return type) we need to make sure the suggested
+ // closure parameter is not a reference in case we suggest `Reverse`. Trying to destructure more
+ // than one level of references would add some extra complexity as we would have to compensate
+ // in the closure body.
+
if_chain! {
if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind;
if let name = name_ident.ident.name.to_ident_string();
if name == "sort_by" || name == "sort_unstable_by";
if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args;
- if utils::match_type(cx, &cx.typeck_results().expr_ty(vec), &paths::VEC);
+ let vec_ty = cx.typeck_results().expr_ty(vec);
+ if utils::is_type_diagnostic_item(cx, vec_ty, sym!(vec_type));
+ let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); // T in Vec<T>
+ if !matches!(&ty.kind(), ty::Ref(..));
+ if utils::is_copy(cx, ty);
if let closure_body = cx.tcx.hir().body(*closure_body_id);
if let &[
Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
if let Some(def_id) = utils::fn_def_id(cx, expr) {
let output = cx.tcx.fn_sig(def_id).output();
let ty = output.skip_binder();
- return matches!(ty.kind, ty::Ref(..))
+ return matches!(ty.kind(), ty::Ref(..))
|| ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
}
let a = cx.typeck_results().expr_ty(e);
let b = cx.typeck_results().expr_ty(&args[0]);
if is_type_diagnostic_item(cx, a, sym!(result_type));
- if let ty::Adt(_, substs) = a.kind;
+ if let ty::Adt(_, substs) = a.kind();
if let Some(a_type) = substs.types().next();
if TyS::same_type(a_type, b);
if_chain! {
if match_def_path(cx, def_id, &paths::TRY_FROM);
if is_type_diagnostic_item(cx, a, sym!(result_type));
- if let ty::Adt(_, substs) = a.kind;
+ if let ty::Adt(_, substs) = a.kind();
if let Some(a_type) = substs.types().next();
if TyS::same_type(a_type, b);
(Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
(Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r),
(Empty, Empty) => true,
- (MacCall(l), MacCall(r)) => l.1 == r.1 && eq_mac_call(&l.0, &r.0) && over(&l.2, &r.2, |l, r| eq_attr(l, r)),
+ (MacCall(l), MacCall(r)) => {
+ l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+ },
_ => false,
}
}
"IPv4", "IPv6",
"ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
"NaN", "NaNs",
- "OAuth",
+ "OAuth", "GraphQL",
"OCaml",
"OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap",
"TensorFlow",
pub mod ptr;
pub mod sugg;
pub mod usage;
+
pub use self::attrs::*;
pub use self::diagnostics::*;
pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::map::Map;
-use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
+use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
use rustc_mir::const_eval;
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::original_sp;
false
}
}
+
// If the snippet is empty, it's an attribute that was inserted during macro
// expansion and we want to ignore those, because they could come from external
// sources that the user has no control over.
/// Checks if type is struct, enum or union type with the given def path.
pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
- match ty.kind {
+ match ty.kind() {
ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
_ => false,
}
/// Checks if the type is equal to a diagnostic item
pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
- match ty.kind {
+ match ty.kind() {
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
_ => false,
}
/// Checks if the type is equal to a lang item
pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
- match ty.kind {
+ match ty.kind() {
ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did,
_ => false,
}
) -> Cow<'a, str> {
let snip = snippet(cx, span, default);
let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
- trim_multiline(snip, true, indent)
+ reindent_multiline(snip, true, indent)
}
/// Same as `snippet_block`, but adapts the applicability level by the rules of
) -> Cow<'a, str> {
let snip = snippet_with_applicability(cx, span, default, applicability);
let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
- trim_multiline(snip, true, indent)
+ reindent_multiline(snip, true, indent)
}
/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
}
}
-/// Trim indentation from a multiline string with possibility of ignoring the
-/// first line.
-fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
- let s_space = trim_multiline_inner(s, ignore_first, indent, ' ');
- let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t');
- trim_multiline_inner(s_tab, ignore_first, indent, ' ')
+/// Reindent a multiline string with possibility of ignoring the first line.
+#[allow(clippy::needless_pass_by_value)]
+pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
+ let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' ');
+ let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t');
+ reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into()
}
-fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>, ch: char) -> Cow<'_, str> {
- let mut x = s
+fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
+ let x = s
.lines()
.skip(ignore_first as usize)
.filter_map(|l| {
})
.min()
.unwrap_or(0);
- if let Some(indent) = indent {
- x = x.saturating_sub(indent);
- }
- if x > 0 {
- Cow::Owned(
- s.lines()
- .enumerate()
- .map(|(i, l)| {
- if (ignore_first && i == 0) || l.is_empty() {
- l
- } else {
- l.split_at(x).1
- }
- })
- .collect::<Vec<_>>()
- .join("\n"),
- )
- } else {
- s
- }
+ let indent = indent.unwrap_or(0);
+ s.lines()
+ .enumerate()
+ .map(|(i, l)| {
+ if (ignore_first && i == 0) || l.is_empty() {
+ l.to_owned()
+ } else if x > indent {
+ l.split_at(x - indent).1.to_owned()
+ } else {
+ " ".repeat(indent - x) + l
+ }
+ })
+ .collect::<Vec<String>>()
+ .join("\n")
}
/// Gets the parent expression, if any –- this is useful to constrain a lint.
/// Returns the base type for references and raw pointers.
pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> {
- match ty.kind {
+ match ty.kind() {
ty::Ref(_, ty, _) => walk_ptrs_ty(ty),
_ => ty,
}
/// depth.
pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
- match ty.kind {
+ match ty.kind() {
ty::Ref(_, ty, _) => inner(ty, depth + 1),
_ => (ty, depth),
}
cx.tcx.erase_late_bound_regions(&ret_ty)
}
+/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
+pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool {
+ ty.walk().any(|inner| match inner.unpack() {
+ GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty),
+ GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+ })
+}
+
/// Returns `true` if the given type is an `unsafe` function.
pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
- match ty.kind {
+ match ty.kind() {
ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
_ => false,
}
return match res {
def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
// FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210
- def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => {
+ def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => {
const_eval::is_const_fn(cx.tcx, def_id)
},
def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat))
},
PatKind::Slice(ref head, ref middle, ref tail) => {
- match &cx.typeck_results().node_type(pat.hir_id).kind {
+ match &cx.typeck_results().node_type(pat.hir_id).kind() {
ty::Slice(..) => {
// [..] is the only irrefutable slice pattern.
!head.is_empty() || middle.is_none() || !tail.is_empty()
&paths::RECEIVER,
];
- let ty_to_check = match probably_ref_ty.kind {
+ let ty_to_check = match probably_ref_ty.kind() {
ty::Ref(_, ty_to_check, _) => ty_to_check,
_ => probably_ref_ty,
};
- let def_id = match ty_to_check.kind {
+ let def_id = match ty_to_check.kind() {
ty::Array(..) => return Some("array"),
ty::Slice(..) => return Some("slice"),
ty::Adt(adt, _) => adt.did,
// Returns whether the type has #[must_use] attribute
pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
- match ty.kind {
+ match ty.kind() {
ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(),
ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(),
ty::Slice(ref ty)
/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point
/// number type, a str, or an array, slice, or tuple of those types).
pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
- match ty.kind {
+ match ty.kind() {
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
- ty::Ref(_, inner, _) if inner.kind == ty::Str => true,
+ ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
_ => false,
/// `is_recursively_primitive_type` function) and None otherwise.
pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
- let expr_kind = &expr_type.kind;
+ let expr_kind = expr_type.kind();
let is_primitive = match expr_kind {
- ty::Slice(ref element_type)
- | ty::Ref(
- _,
- ty::TyS {
- kind: ty::Slice(ref element_type),
- ..
- },
- _,
- ) => is_recursively_primitive_type(element_type),
+ ty::Slice(element_type) => is_recursively_primitive_type(element_type),
+ ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &ty::Slice(_)) => {
+ if let ty::Slice(element_type) = inner_ty.kind() {
+ is_recursively_primitive_type(element_type)
+ } else {
+ unreachable!()
+ }
+ },
_ => false,
};
if is_primitive {
// if we have wrappers like Array, Slice or Tuple, print these
// and get the type enclosed in the slice ref
- match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind {
+ match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
ty::Slice(..) => return Some("slice".into()),
ty::Array(..) => return Some("array".into()),
ty::Tuple(..) => return Some("tuple".into()),
#[cfg(test)]
mod test {
- use super::{trim_multiline, without_block_comments};
+ use super::{reindent_multiline, without_block_comments};
#[test]
- fn test_trim_multiline_single_line() {
- assert_eq!("", trim_multiline("".into(), false, None));
- assert_eq!("...", trim_multiline("...".into(), false, None));
- assert_eq!("...", trim_multiline(" ...".into(), false, None));
- assert_eq!("...", trim_multiline("\t...".into(), false, None));
- assert_eq!("...", trim_multiline("\t\t...".into(), false, None));
+ fn test_reindent_multiline_single_line() {
+ assert_eq!("", reindent_multiline("".into(), false, None));
+ assert_eq!("...", reindent_multiline("...".into(), false, None));
+ assert_eq!("...", reindent_multiline(" ...".into(), false, None));
+ assert_eq!("...", reindent_multiline("\t...".into(), false, None));
+ assert_eq!("...", reindent_multiline("\t\t...".into(), false, None));
}
#[test]
#[rustfmt::skip]
- fn test_trim_multiline_block() {
+ fn test_reindent_multiline_block() {
assert_eq!("\
if x {
y
} else {
z
- }", trim_multiline(" if x {
+ }", reindent_multiline(" if x {
y
} else {
z
\ty
} else {
\tz
- }", trim_multiline(" if x {
+ }", reindent_multiline(" if x {
\ty
} else {
\tz
#[test]
#[rustfmt::skip]
- fn test_trim_multiline_empty_line() {
+ fn test_reindent_multiline_empty_line() {
assert_eq!("\
if x {
y
} else {
z
- }", trim_multiline(" if x {
+ }", reindent_multiline(" if x {
y
} else {
}".into(), false, None));
}
+ #[test]
+ #[rustfmt::skip]
+ fn test_reindent_multiline_lines_deeper() {
+ assert_eq!("\
+ if x {
+ y
+ } else {
+ z
+ }", reindent_multiline("\
+ if x {
+ y
+ } else {
+ z
+ }".into(), true, Some(8)));
+ }
+
#[test]
fn test_without_block_comments_lines_without_block_comments() {
let result = without_block_comments(vec!["/*", "", "*/"]);
pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
use rustc_ast::ast::RangeLimits;
- let snippet = snippet(cx, expr.span, default);
+ let snippet = if expr.span.from_expansion() {
+ snippet_with_macro_callsite(cx, expr.span, default)
+ } else {
+ snippet(cx, expr.span, default)
+ };
match expr.kind {
ast::ExprKind::AddrOf(..)
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// search for `&vec![_]` expressions where the adjusted type is `&[_]`
if_chain! {
- if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind;
- if let ty::Slice(..) = ty.kind;
+ if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
+ if let ty::Slice(..) = ty.kind();
if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind;
if let Some(vec_args) = higher::vec_macro(cx, addressee);
then {
/// Returns the item type of the vector (i.e., the `T` in `Vec<T>`).
fn vec_type(ty: Ty<'_>) -> Ty<'_> {
- if let ty::Adt(_, substs) = ty.kind {
+ if let ty::Adt(_, substs) = ty.kind() {
substs.type_at(0)
} else {
panic!("The type of `vec!` is a not a struct?");
deprecation: None,
module: "assign_ops",
},
+ Lint {
+ name: "async_yields_async",
+ group: "correctness",
+ desc: "async blocks that return a type that can be awaited",
+ deprecation: None,
+ module: "async_yields_async",
+ },
Lint {
name: "await_holding_lock",
group: "pedantic",
-error: this function has a large number of lines
+error: this function has too many lines (2/1)
--> $DIR/test.rs:18:1
|
LL | / fn too_many_lines() {
|
= note: `-D clippy::too-many-lines` implied by `-D warnings`
-error: this function has a large number of lines
+error: this function has too many lines (2/1)
--> $DIR/test.rs:38:1
|
LL | / fn comment_before_code() {
--- /dev/null
+// run-rustfix
+// edition:2018
+
+#![feature(async_closure)]
+#![warn(clippy::async_yields_async)]
+
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+struct CustomFutureType;
+
+impl Future for CustomFutureType {
+ type Output = u8;
+
+ fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll<Self::Output> {
+ Poll::Ready(3)
+ }
+}
+
+fn custom_future_type_ctor() -> CustomFutureType {
+ CustomFutureType
+}
+
+async fn f() -> CustomFutureType {
+ // Don't warn for functions since you have to explicitly declare their
+ // return types.
+ CustomFutureType
+}
+
+#[rustfmt::skip]
+fn main() {
+ let _f = {
+ 3
+ };
+ let _g = async {
+ 3
+ };
+ let _h = async {
+ async {
+ 3
+ }.await
+ };
+ let _i = async {
+ CustomFutureType.await
+ };
+ let _i = async || {
+ 3
+ };
+ let _j = async || {
+ async {
+ 3
+ }.await
+ };
+ let _k = async || {
+ CustomFutureType.await
+ };
+ let _l = async || CustomFutureType.await;
+ let _m = async || {
+ println!("I'm bored");
+ // Some more stuff
+
+ // Finally something to await
+ CustomFutureType.await
+ };
+ let _n = async || custom_future_type_ctor();
+ let _o = async || f();
+}
--- /dev/null
+// run-rustfix
+// edition:2018
+
+#![feature(async_closure)]
+#![warn(clippy::async_yields_async)]
+
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+struct CustomFutureType;
+
+impl Future for CustomFutureType {
+ type Output = u8;
+
+ fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll<Self::Output> {
+ Poll::Ready(3)
+ }
+}
+
+fn custom_future_type_ctor() -> CustomFutureType {
+ CustomFutureType
+}
+
+async fn f() -> CustomFutureType {
+ // Don't warn for functions since you have to explicitly declare their
+ // return types.
+ CustomFutureType
+}
+
+#[rustfmt::skip]
+fn main() {
+ let _f = {
+ 3
+ };
+ let _g = async {
+ 3
+ };
+ let _h = async {
+ async {
+ 3
+ }
+ };
+ let _i = async {
+ CustomFutureType
+ };
+ let _i = async || {
+ 3
+ };
+ let _j = async || {
+ async {
+ 3
+ }
+ };
+ let _k = async || {
+ CustomFutureType
+ };
+ let _l = async || CustomFutureType;
+ let _m = async || {
+ println!("I'm bored");
+ // Some more stuff
+
+ // Finally something to await
+ CustomFutureType
+ };
+ let _n = async || custom_future_type_ctor();
+ let _o = async || f();
+}
--- /dev/null
+error: an async construct yields a type which is itself awaitable
+ --> $DIR/async_yields_async.rs:40:9
+ |
+LL | let _h = async {
+ | ____________________-
+LL | | async {
+ | |_________^
+LL | || 3
+LL | || }
+ | ||_________^ awaitable value not awaited
+LL | | };
+ | |_____- outer async construct
+ |
+ = note: `-D clippy::async-yields-async` implied by `-D warnings`
+help: consider awaiting this value
+ |
+LL | async {
+LL | 3
+LL | }.await
+ |
+
+error: an async construct yields a type which is itself awaitable
+ --> $DIR/async_yields_async.rs:45:9
+ |
+LL | let _i = async {
+ | ____________________-
+LL | | CustomFutureType
+ | | ^^^^^^^^^^^^^^^^
+ | | |
+ | | awaitable value not awaited
+ | | help: consider awaiting this value: `CustomFutureType.await`
+LL | | };
+ | |_____- outer async construct
+
+error: an async construct yields a type which is itself awaitable
+ --> $DIR/async_yields_async.rs:51:9
+ |
+LL | let _j = async || {
+ | _______________________-
+LL | | async {
+ | |_________^
+LL | || 3
+LL | || }
+ | ||_________^ awaitable value not awaited
+LL | | };
+ | |_____- outer async construct
+ |
+help: consider awaiting this value
+ |
+LL | async {
+LL | 3
+LL | }.await
+ |
+
+error: an async construct yields a type which is itself awaitable
+ --> $DIR/async_yields_async.rs:56:9
+ |
+LL | let _k = async || {
+ | _______________________-
+LL | | CustomFutureType
+ | | ^^^^^^^^^^^^^^^^
+ | | |
+ | | awaitable value not awaited
+ | | help: consider awaiting this value: `CustomFutureType.await`
+LL | | };
+ | |_____- outer async construct
+
+error: an async construct yields a type which is itself awaitable
+ --> $DIR/async_yields_async.rs:58:23
+ |
+LL | let _l = async || CustomFutureType;
+ | ^^^^^^^^^^^^^^^^
+ | |
+ | outer async construct
+ | awaitable value not awaited
+ | help: consider awaiting this value: `CustomFutureType.await`
+
+error: an async construct yields a type which is itself awaitable
+ --> $DIR/async_yields_async.rs:64:9
+ |
+LL | let _m = async || {
+ | _______________________-
+LL | | println!("I'm bored");
+LL | | // Some more stuff
+LL | |
+LL | | // Finally something to await
+LL | | CustomFutureType
+ | | ^^^^^^^^^^^^^^^^
+ | | |
+ | | awaitable value not awaited
+ | | help: consider awaiting this value: `CustomFutureType.await`
+LL | | };
+ | |_____- outer async construct
+
+error: aborting due to 6 previous errors
+
#![warn(clippy::borrow_interior_mutable_const)]
#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)]
+#![allow(const_item_mutation)]
use std::borrow::Cow;
use std::cell::{Cell, UnsafeCell};
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:65:5
+ --> $DIR/borrow_interior_mutable_const.rs:66:5
|
LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
| ^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:66:16
+ --> $DIR/borrow_interior_mutable_const.rs:67:16
|
LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
| ^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:69:22
+ --> $DIR/borrow_interior_mutable_const.rs:70:22
|
LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:70:25
+ --> $DIR/borrow_interior_mutable_const.rs:71:25
|
LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:71:27
+ --> $DIR/borrow_interior_mutable_const.rs:72:27
|
LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:72:26
+ --> $DIR/borrow_interior_mutable_const.rs:73:26
|
LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:83:14
+ --> $DIR/borrow_interior_mutable_const.rs:84:14
|
LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
| ^^^^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:84:14
+ --> $DIR/borrow_interior_mutable_const.rs:85:14
|
LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
| ^^^^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:85:19
+ --> $DIR/borrow_interior_mutable_const.rs:86:19
|
LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
| ^^^^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:86:14
+ --> $DIR/borrow_interior_mutable_const.rs:87:14
|
LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
| ^^^^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:87:13
+ --> $DIR/borrow_interior_mutable_const.rs:88:13
|
LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
| ^^^^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:93:13
+ --> $DIR/borrow_interior_mutable_const.rs:94:13
|
LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
| ^^^^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:98:5
+ --> $DIR/borrow_interior_mutable_const.rs:99:5
|
LL | CELL.set(2); //~ ERROR interior mutability
| ^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:99:16
+ --> $DIR/borrow_interior_mutable_const.rs:100:16
|
LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
| ^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:112:5
+ --> $DIR/borrow_interior_mutable_const.rs:113:5
|
LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
| ^^^^^^^^^^^
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
- --> $DIR/borrow_interior_mutable_const.rs:113:16
+ --> $DIR/borrow_interior_mutable_const.rs:114:16
|
LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
| ^^^^^^^^^^^
if truth() {}
}
}
+
+ // Fix #5962
+ if matches!(true, true) && matches!(true, true) {}
}
if truth() {}
}
}
+
+ // Fix #5962
+ if matches!(true, true) {
+ if matches!(true, true) {}
+ }
}
LL | }
|
-error: aborting due to 7 previous errors
+error: this `if` statement can be collapsed
+ --> $DIR/collapsible_if.rs:154:5
+ |
+LL | / if matches!(true, true) {
+LL | | if matches!(true, true) {}
+LL | | }
+ | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}`
+
+error: aborting due to 8 previous errors
--- /dev/null
+// run-rustfix
+
+#![allow(unused_imports)]
+#![deny(clippy::default_trait_access)]
+
+use std::default;
+use std::default::Default as D2;
+use std::string;
+
+fn main() {
+ let s1: String = std::string::String::default();
+
+ let s2 = String::default();
+
+ let s3: String = std::string::String::default();
+
+ let s4: String = std::string::String::default();
+
+ let s5 = string::String::default();
+
+ let s6: String = std::string::String::default();
+
+ let s7 = std::string::String::default();
+
+ let s8: String = DefaultFactory::make_t_badly();
+
+ let s9: String = DefaultFactory::make_t_nicely();
+
+ let s10 = DerivedDefault::default();
+
+ let s11: GenericDerivedDefault<String> = GenericDerivedDefault::default();
+
+ let s12 = GenericDerivedDefault::<String>::default();
+
+ let s13 = TupleDerivedDefault::default();
+
+ let s14: TupleDerivedDefault = TupleDerivedDefault::default();
+
+ let s15: ArrayDerivedDefault = ArrayDerivedDefault::default();
+
+ let s16 = ArrayDerivedDefault::default();
+
+ let s17: TupleStructDerivedDefault = TupleStructDerivedDefault::default();
+
+ let s18 = TupleStructDerivedDefault::default();
+
+ let s19 = <DerivedDefault as Default>::default();
+
+ println!(
+ "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]",
+ s1,
+ s2,
+ s3,
+ s4,
+ s5,
+ s6,
+ s7,
+ s8,
+ s9,
+ s10,
+ s11,
+ s12,
+ s13,
+ s14,
+ s15,
+ s16,
+ s17,
+ s18,
+ s19,
+ );
+}
+
+struct DefaultFactory;
+
+impl DefaultFactory {
+ pub fn make_t_badly<T: Default>() -> T {
+ Default::default()
+ }
+
+ pub fn make_t_nicely<T: Default>() -> T {
+ T::default()
+ }
+}
+
+#[derive(Debug, Default)]
+struct DerivedDefault {
+ pub s: String,
+}
+
+#[derive(Debug, Default)]
+struct GenericDerivedDefault<T: Default + std::fmt::Debug> {
+ pub s: T,
+}
+
+#[derive(Debug, Default)]
+struct TupleDerivedDefault {
+ pub s: (String, String),
+}
+
+#[derive(Debug, Default)]
+struct ArrayDerivedDefault {
+ pub s: [String; 10],
+}
+
+#[derive(Debug, Default)]
+struct TupleStructDerivedDefault(String);
-#![warn(clippy::default_trait_access)]
+// run-rustfix
+
+#![allow(unused_imports)]
+#![deny(clippy::default_trait_access)]
use std::default;
use std::default::Default as D2;
error: calling `std::string::String::default()` is more clear than this expression
- --> $DIR/default_trait_access.rs:8:22
+ --> $DIR/default_trait_access.rs:11:22
|
LL | let s1: String = Default::default();
| ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
|
- = note: `-D clippy::default-trait-access` implied by `-D warnings`
+note: the lint level is defined here
+ --> $DIR/default_trait_access.rs:4:9
+ |
+LL | #![deny(clippy::default_trait_access)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: calling `std::string::String::default()` is more clear than this expression
- --> $DIR/default_trait_access.rs:12:22
+ --> $DIR/default_trait_access.rs:15:22
|
LL | let s3: String = D2::default();
| ^^^^^^^^^^^^^ help: try: `std::string::String::default()`
error: calling `std::string::String::default()` is more clear than this expression
- --> $DIR/default_trait_access.rs:14:22
+ --> $DIR/default_trait_access.rs:17:22
|
LL | let s4: String = std::default::Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
error: calling `std::string::String::default()` is more clear than this expression
- --> $DIR/default_trait_access.rs:18:22
+ --> $DIR/default_trait_access.rs:21:22
|
LL | let s6: String = default::Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
-error: calling `GenericDerivedDefault<std::string::String>::default()` is more clear than this expression
- --> $DIR/default_trait_access.rs:28:46
+error: calling `GenericDerivedDefault::default()` is more clear than this expression
+ --> $DIR/default_trait_access.rs:31:46
|
LL | let s11: GenericDerivedDefault<String> = Default::default();
- | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault<std::string::String>::default()`
+ | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()`
error: calling `TupleDerivedDefault::default()` is more clear than this expression
- --> $DIR/default_trait_access.rs:34:36
+ --> $DIR/default_trait_access.rs:37:36
|
LL | let s14: TupleDerivedDefault = Default::default();
| ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()`
error: calling `ArrayDerivedDefault::default()` is more clear than this expression
- --> $DIR/default_trait_access.rs:36:36
+ --> $DIR/default_trait_access.rs:39:36
|
LL | let s15: ArrayDerivedDefault = Default::default();
| ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()`
error: calling `TupleStructDerivedDefault::default()` is more clear than this expression
- --> $DIR/default_trait_access.rs:40:42
+ --> $DIR/default_trait_access.rs:43:42
|
LL | let s17: TupleStructDerivedDefault = Default::default();
| ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()`
fn test_units() {
}
+/// This tests allowed identifiers.
+/// DirectX
+/// ECMAScript
+/// OAuth GraphQL
+/// TeX LaTeX BibTeX BibLaTeX
+/// CamelCase (see also #2395)
+/// be_sure_we_got_to_the_end_of_it
+fn test_allowed() {
+}
+
/// This test has [a link_with_underscores][chunked-example] inside it. See #823.
/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues)
/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link].
/// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
fn issue_1832() {}
-/// Ok: CamelCase (It should not be surrounded by backticks)
-fn issue_2395() {}
-
/// An iterator over mycrate::Collection's values.
/// It should not lint a `'static` lifetime in ticks.
fn issue_2210() {}
LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
+ --> $DIR/doc.rs:58:5
+ |
+LL | /// be_sure_we_got_to_the_end_of_it
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
error: you should put `link_with_underscores` between ticks in the documentation
- --> $DIR/doc.rs:52:22
+ --> $DIR/doc.rs:62:22
|
LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823.
| ^^^^^^^^^^^^^^^^^^^^^
error: you should put `inline_link2` between ticks in the documentation
- --> $DIR/doc.rs:55:21
+ --> $DIR/doc.rs:65:21
|
LL | /// It can also be [inline_link2].
| ^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
- --> $DIR/doc.rs:65:5
+ --> $DIR/doc.rs:75:5
|
LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `CamelCaseThing` between ticks in the documentation
- --> $DIR/doc.rs:73:8
+ --> $DIR/doc.rs:83:8
|
LL | /// ## CamelCaseThing
| ^^^^^^^^^^^^^^
error: you should put `CamelCaseThing` between ticks in the documentation
- --> $DIR/doc.rs:76:7
+ --> $DIR/doc.rs:86:7
|
LL | /// # CamelCaseThing
| ^^^^^^^^^^^^^^
error: you should put `CamelCaseThing` between ticks in the documentation
- --> $DIR/doc.rs:78:22
+ --> $DIR/doc.rs:88:22
|
LL | /// Not a title #897 CamelCaseThing
| ^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
- --> $DIR/doc.rs:79:5
+ --> $DIR/doc.rs:89:5
|
LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
- --> $DIR/doc.rs:86:5
+ --> $DIR/doc.rs:96:5
|
LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
- --> $DIR/doc.rs:99:5
+ --> $DIR/doc.rs:109:5
|
LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `FooBar` between ticks in the documentation
- --> $DIR/doc.rs:110:43
+ --> $DIR/doc.rs:120:43
|
LL | /** E.g., serialization of an empty list: FooBar
| ^^^^^^
error: you should put `BarQuz` between ticks in the documentation
- --> $DIR/doc.rs:115:5
+ --> $DIR/doc.rs:125:5
|
LL | And BarQuz too.
| ^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
- --> $DIR/doc.rs:116:1
+ --> $DIR/doc.rs:126:1
|
LL | be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `FooBar` between ticks in the documentation
- --> $DIR/doc.rs:121:43
+ --> $DIR/doc.rs:131:43
|
LL | /** E.g., serialization of an empty list: FooBar
| ^^^^^^
error: you should put `BarQuz` between ticks in the documentation
- --> $DIR/doc.rs:126:5
+ --> $DIR/doc.rs:136:5
|
LL | And BarQuz too.
| ^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
- --> $DIR/doc.rs:127:1
+ --> $DIR/doc.rs:137:1
|
LL | be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
- --> $DIR/doc.rs:138:5
+ --> $DIR/doc.rs:148:5
|
LL | /// be_sure_we_got_to_the_end_of_it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put bare URLs between `<`/`>` or make a proper Markdown link
- --> $DIR/doc.rs:165:13
+ --> $DIR/doc.rs:175:13
|
LL | /// Not ok: http://www.unicode.org
| ^^^^^^^^^^^^^^^^^^^^^^
error: you should put bare URLs between `<`/`>` or make a proper Markdown link
- --> $DIR/doc.rs:166:13
+ --> $DIR/doc.rs:176:13
|
LL | /// Not ok: https://www.unicode.org
| ^^^^^^^^^^^^^^^^^^^^^^^
error: you should put bare URLs between `<`/`>` or make a proper Markdown link
- --> $DIR/doc.rs:167:13
+ --> $DIR/doc.rs:177:13
|
LL | /// Not ok: http://www.unicode.org/
| ^^^^^^^^^^^^^^^^^^^^^^
error: you should put bare URLs between `<`/`>` or make a proper Markdown link
- --> $DIR/doc.rs:168:13
+ --> $DIR/doc.rs:178:13
|
LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: you should put `mycrate::Collection` between ticks in the documentation
- --> $DIR/doc.rs:174:22
+ --> $DIR/doc.rs:181:22
|
LL | /// An iterator over mycrate::Collection's values.
| ^^^^^^^^^^^^^^^^^^^
-error: aborting due to 30 previous errors
+error: aborting due to 31 previous errors
-error: this function has a large number of lines
+error: this function has too many lines (102/100)
--> $DIR/functions_maxlines.rs:58:1
|
LL | / fn bad_lines() {
fn main() {
- println!("{}" a); //~ERROR expected token: `,`
+ println!("{}" a); //~ERROR expected `,`, found `a`
}
-error: expected token: `,`
+error: expected `,`, found `a`
--> $DIR/issue-3145.rs:2:19
|
-LL | println!("{}" a); //~ERROR expected token: `,`
+LL | println!("{}" a); //~ERROR expected `,`, found `a`
| ^ expected `,`
error: aborting due to previous error
}
}
-struct MutPointerReturnerOk2;
+struct ConstPointerReturnerOk2;
-impl MutPointerReturnerOk2 {
+impl ConstPointerReturnerOk2 {
// should not trigger lint
pub fn new() -> *const Self {
unimplemented!();
unimplemented!();
}
}
+
+mod issue5435 {
+ struct V;
+
+ pub trait TraitRetSelf {
+ // should not trigger lint
+ fn new() -> Self;
+ }
+
+ pub trait TraitRet {
+ // should trigger lint as we are in trait definition
+ fn new() -> String;
+ }
+ pub struct StructRet;
+ impl TraitRet for StructRet {
+ // should not trigger lint as we are in the impl block
+ fn new() -> String {
+ unimplemented!();
+ }
+ }
+
+ pub trait TraitRet2 {
+ // should trigger lint
+ fn new(_: String) -> String;
+ }
+
+ trait TupleReturnerOk {
+ // should not trigger lint
+ fn new() -> (Self, u32)
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait TupleReturnerOk2 {
+ // should not trigger lint (it doesn't matter which element in the tuple is Self)
+ fn new() -> (u32, Self)
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait TupleReturnerOk3 {
+ // should not trigger lint (tuple can contain multiple Self)
+ fn new() -> (Self, Self)
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait TupleReturnerBad {
+ // should trigger lint
+ fn new() -> (u32, u32) {
+ unimplemented!();
+ }
+ }
+
+ trait MutPointerReturnerOk {
+ // should not trigger lint
+ fn new() -> *mut Self
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait ConstPointerReturnerOk2 {
+ // should not trigger lint
+ fn new() -> *const Self
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait MutPointerReturnerBad {
+ // should trigger lint
+ fn new() -> *mut V {
+ unimplemented!();
+ }
+ }
+
+ trait GenericReturnerOk {
+ // should not trigger lint
+ fn new() -> Option<Self>
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait NestedReturnerOk {
+ // should not trigger lint
+ fn new() -> (Option<Self>, u32)
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait NestedReturnerOk2 {
+ // should not trigger lint
+ fn new() -> ((Self, u32), u32)
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+
+ trait NestedReturnerOk3 {
+ // should not trigger lint
+ fn new() -> Option<(Self, u32)>
+ where
+ Self: Sized,
+ {
+ unimplemented!();
+ }
+ }
+}
LL | | }
| |_____^
-error: aborting due to 6 previous errors
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:224:9
+ |
+LL | fn new() -> String;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:236:9
+ |
+LL | fn new(_: String) -> String;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:271:9
+ |
+LL | / fn new() -> (u32, u32) {
+LL | | unimplemented!();
+LL | | }
+ | |_________^
+
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:298:9
+ |
+LL | / fn new() -> *mut V {
+LL | | unimplemented!();
+LL | | }
+ | |_________^
+
+error: aborting due to 10 previous errors
-error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:38:5
|
LL | x.field.map(do_nothing);
|
= note: `-D clippy::option-map-unit-fn` implied by `-D warnings`
-error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:40:5
|
LL | x.field.map(do_nothing);
| |
| help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }`
-error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:42:5
|
LL | x.field.map(diverge);
| |
| help: try this: `if let Some(x_field) = x.field { diverge(x_field) }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:48:5
|
LL | x.field.map(|value| x.do_option_nothing(value + captured));
| |
| help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:50:5
|
LL | x.field.map(|value| { x.do_option_plus_one(value + captured); });
| |
| help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:53:5
|
LL | x.field.map(|value| do_nothing(value + captured));
| |
| help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:55:5
|
LL | x.field.map(|value| { do_nothing(value + captured) });
| |
| help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:57:5
|
LL | x.field.map(|value| { do_nothing(value + captured); });
| |
| help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:59:5
|
LL | x.field.map(|value| { { do_nothing(value + captured); } });
| |
| help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:62:5
|
LL | x.field.map(|value| diverge(value + captured));
| |
| help: try this: `if let Some(value) = x.field { diverge(value + captured) }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:64:5
|
LL | x.field.map(|value| { diverge(value + captured) });
| |
| help: try this: `if let Some(value) = x.field { diverge(value + captured) }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:66:5
|
LL | x.field.map(|value| { diverge(value + captured); });
| |
| help: try this: `if let Some(value) = x.field { diverge(value + captured); }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:68:5
|
LL | x.field.map(|value| { { diverge(value + captured); } });
| |
| help: try this: `if let Some(value) = x.field { diverge(value + captured); }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:73:5
|
LL | x.field.map(|value| { let y = plus_one(value + captured); });
| |
| help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:75:5
|
LL | x.field.map(|value| { plus_one(value + captured); });
| |
| help: try this: `if let Some(value) = x.field { plus_one(value + captured); }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:77:5
|
LL | x.field.map(|value| { { plus_one(value + captured); } });
| |
| help: try this: `if let Some(value) = x.field { plus_one(value + captured); }`
-error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:80:5
|
LL | x.field.map(|ref value| { do_nothing(value + captured) });
| |
| help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }`
-error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type
+error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()`
--> $DIR/option_map_unit_fn_fixable.rs:82:5
|
LL | option().map(do_nothing);}
let without_default = Some(Foo);
without_default.unwrap_or_else(Foo::new);
- let mut map = HashMap::<u64, String>::new();
- map.entry(42).or_insert_with(String::new);
-
- let mut btree = BTreeMap::<u64, String>::new();
- btree.entry(42).or_insert_with(String::new);
-
let stringy = Some(String::from(""));
let _ = stringy.unwrap_or_else(|| "".to_owned());
Some(42)
}
let _ = None.or(foo());
+
+ // See issue #5693.
+ let mut map = std::collections::HashMap::new();
+ map.insert(1, vec![1]);
+ map.entry(1).or_insert(vec![]);
+
+ let mut map = HashMap::<u64, String>::new();
+ map.entry(42).or_insert(String::new());
+
+ let mut btree = BTreeMap::<u64, String>::new();
+ btree.entry(42).or_insert(String::new());
}
fn main() {}
let without_default = Some(Foo);
without_default.unwrap_or(Foo::new());
- let mut map = HashMap::<u64, String>::new();
- map.entry(42).or_insert(String::new());
-
- let mut btree = BTreeMap::<u64, String>::new();
- btree.entry(42).or_insert(String::new());
-
let stringy = Some(String::from(""));
let _ = stringy.unwrap_or("".to_owned());
Some(42)
}
let _ = None.or(foo());
+
+ // See issue #5693.
+ let mut map = std::collections::HashMap::new();
+ map.insert(1, vec![1]);
+ map.entry(1).or_insert(vec![]);
+
+ let mut map = HashMap::<u64, String>::new();
+ map.entry(42).or_insert(String::new());
+
+ let mut btree = BTreeMap::<u64, String>::new();
+ btree.entry(42).or_insert(String::new());
}
fn main() {}
LL | without_default.unwrap_or(Foo::new());
| ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
-error: use of `or_insert` followed by a function call
- --> $DIR/or_fun_call.rs:62:19
- |
-LL | map.entry(42).or_insert(String::new());
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
-
-error: use of `or_insert` followed by a function call
- --> $DIR/or_fun_call.rs:65:21
- |
-LL | btree.entry(42).or_insert(String::new());
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
-
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:68:21
+ --> $DIR/or_fun_call.rs:62:21
|
LL | let _ = stringy.unwrap_or("".to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())`
error: use of `or` followed by a function call
- --> $DIR/or_fun_call.rs:93:35
+ --> $DIR/or_fun_call.rs:87:35
|
LL | let _ = Some("a".to_string()).or(Some("b".to_string()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))`
error: use of `or` followed by a function call
- --> $DIR/or_fun_call.rs:97:10
+ --> $DIR/or_fun_call.rs:91:10
|
LL | .or(Some(Bar(b, Duration::from_secs(2))));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))`
-error: aborting due to 15 previous errors
+error: aborting due to 13 previous errors
-error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:35:5
|
LL | x.field.map(do_nothing);
|
= note: `-D clippy::result-map-unit-fn` implied by `-D warnings`
-error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:37:5
|
LL | x.field.map(do_nothing);
| |
| help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }`
-error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:39:5
|
LL | x.field.map(diverge);
| |
| help: try this: `if let Ok(x_field) = x.field { diverge(x_field) }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:45:5
|
LL | x.field.map(|value| x.do_result_nothing(value + captured));
| |
| help: try this: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:47:5
|
LL | x.field.map(|value| { x.do_result_plus_one(value + captured); });
| |
| help: try this: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:50:5
|
LL | x.field.map(|value| do_nothing(value + captured));
| |
| help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:52:5
|
LL | x.field.map(|value| { do_nothing(value + captured) });
| |
| help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:54:5
|
LL | x.field.map(|value| { do_nothing(value + captured); });
| |
| help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:56:5
|
LL | x.field.map(|value| { { do_nothing(value + captured); } });
| |
| help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:59:5
|
LL | x.field.map(|value| diverge(value + captured));
| |
| help: try this: `if let Ok(value) = x.field { diverge(value + captured) }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:61:5
|
LL | x.field.map(|value| { diverge(value + captured) });
| |
| help: try this: `if let Ok(value) = x.field { diverge(value + captured) }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:63:5
|
LL | x.field.map(|value| { diverge(value + captured); });
| |
| help: try this: `if let Ok(value) = x.field { diverge(value + captured); }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:65:5
|
LL | x.field.map(|value| { { diverge(value + captured); } });
| |
| help: try this: `if let Ok(value) = x.field { diverge(value + captured); }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:70:5
|
LL | x.field.map(|value| { let y = plus_one(value + captured); });
| |
| help: try this: `if let Ok(value) = x.field { let y = plus_one(value + captured); }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:72:5
|
LL | x.field.map(|value| { plus_one(value + captured); });
| |
| help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:74:5
|
LL | x.field.map(|value| { { plus_one(value + captured); } });
| |
| help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_fixable.rs:77:5
|
LL | x.field.map(|ref value| { do_nothing(value + captured) });
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_unfixable.rs:23:5
|
LL | x.field.map(|value| { do_nothing(value); do_nothing(value) });
|
= note: `-D clippy::result-map-unit-fn` implied by `-D warnings`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_unfixable.rs:25:5
|
LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) });
| |
| help: try this: `if let Ok(value) = x.field { ... }`
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_unfixable.rs:29:5
|
LL | x.field.map(|value| {
| |_______|
|
-error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
--> $DIR/result_map_unit_fn_unfixable.rs:33:5
|
LL | x.field.map(|value| { do_nothing(value); do_nothing(value); });
| |
| help: try this: `if let Ok(value) = x.field { ... }`
-error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()`
--> $DIR/result_map_unit_fn_unfixable.rs:37:5
|
LL | "12".parse::<i32>().map(diverge);
| |
| help: try this: `if let Ok(a) = "12".parse::<i32>() { diverge(a) }`
-error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
+error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()`
--> $DIR/result_map_unit_fn_unfixable.rs:43:5
|
LL | y.map(do_nothing);
#![warn(clippy::same_item_push)]
+const VALUE: u8 = 7;
+
fn mutate_increment(x: &mut u8) -> u8 {
*x += 1;
*x
x + 1
}
-fn main() {
- // Test for basic case
- let mut spaces = Vec::with_capacity(10);
- for _ in 0..10 {
- spaces.push(vec![b' ']);
- }
+fn fun() -> usize {
+ 42
+}
- let mut vec2: Vec<u8> = Vec::new();
+fn main() {
+ // ** linted cases **
+ let mut vec: Vec<u8> = Vec::new();
let item = 2;
for _ in 5..=20 {
- vec2.push(item);
+ vec.push(item);
}
- let mut vec3: Vec<u8> = Vec::new();
+ let mut vec: Vec<u8> = Vec::new();
for _ in 0..15 {
let item = 2;
- vec3.push(item);
+ vec.push(item);
}
- let mut vec4: Vec<u8> = Vec::new();
+ let mut vec: Vec<u8> = Vec::new();
for _ in 0..15 {
- vec4.push(13);
+ vec.push(13);
+ }
+
+ let mut vec = Vec::new();
+ for _ in 0..20 {
+ vec.push(VALUE);
+ }
+
+ let mut vec = Vec::new();
+ let item = VALUE;
+ for _ in 0..20 {
+ vec.push(item);
+ }
+
+ // ** non-linted cases **
+ let mut spaces = Vec::with_capacity(10);
+ for _ in 0..10 {
+ spaces.push(vec![b' ']);
}
// Suggestion should not be given as pushed variable can mutate
- let mut vec5: Vec<u8> = Vec::new();
+ let mut vec: Vec<u8> = Vec::new();
let mut item: u8 = 2;
for _ in 0..30 {
- vec5.push(mutate_increment(&mut item));
+ vec.push(mutate_increment(&mut item));
}
- let mut vec6: Vec<u8> = Vec::new();
+ let mut vec: Vec<u8> = Vec::new();
let mut item: u8 = 2;
let mut item2 = &mut mutate_increment(&mut item);
for _ in 0..30 {
- vec6.push(mutate_increment(item2));
+ vec.push(mutate_increment(item2));
}
- let mut vec7: Vec<usize> = Vec::new();
+ let mut vec: Vec<usize> = Vec::new();
for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() {
- vec7.push(a);
+ vec.push(a);
}
- let mut vec8: Vec<u8> = Vec::new();
+ let mut vec: Vec<u8> = Vec::new();
for i in 0..30 {
- vec8.push(increment(i));
+ vec.push(increment(i));
}
- let mut vec9: Vec<u8> = Vec::new();
+ let mut vec: Vec<u8> = Vec::new();
for i in 0..30 {
- vec9.push(i + i * i);
+ vec.push(i + i * i);
}
// Suggestion should not be given as there are multiple pushes that are not the same
- let mut vec10: Vec<u8> = Vec::new();
+ let mut vec: Vec<u8> = Vec::new();
let item: u8 = 2;
for _ in 0..30 {
- vec10.push(item);
- vec10.push(item * 2);
+ vec.push(item);
+ vec.push(item * 2);
}
// Suggestion should not be given as Vec is not involved
for i in 0..30 {
vec_a.push(A { kind: i });
}
- let mut vec12: Vec<u8> = Vec::new();
+ let mut vec: Vec<u8> = Vec::new();
for a in vec_a {
- vec12.push(2u8.pow(a.kind));
+ vec.push(2u8.pow(a.kind));
}
// Fix #5902
- let mut vec13: Vec<u8> = Vec::new();
+ let mut vec: Vec<u8> = Vec::new();
let mut item = 0;
for _ in 0..10 {
- vec13.push(item);
+ vec.push(item);
item += 10;
}
+
+ // Fix #5979
+ let mut vec: Vec<std::fs::File> = Vec::new();
+ for _ in 0..10 {
+ vec.push(std::fs::File::open("foobar").unwrap());
+ }
+ // Fix #5979
+ #[derive(Clone)]
+ struct S {}
+
+ trait T {}
+ impl T for S {}
+
+ let mut vec: Vec<Box<dyn T>> = Vec::new();
+ for _ in 0..10 {
+ vec.push(Box::new(S {}));
+ }
+
+ // Fix #5985
+ let mut vec = Vec::new();
+ let item = 42;
+ let item = fun();
+ for _ in 0..20 {
+ vec.push(item);
+ }
+
+ // Fix #5985
+ let mut vec = Vec::new();
+ let key = 1;
+ for _ in 0..20 {
+ let item = match key {
+ 1 => 10,
+ _ => 0,
+ };
+ vec.push(item);
+ }
}
error: it looks like the same item is being pushed into this Vec
- --> $DIR/same_item_push.rs:16:9
+ --> $DIR/same_item_push.rs:23:9
|
-LL | spaces.push(vec![b' ']);
- | ^^^^^^
+LL | vec.push(item);
+ | ^^^
|
= note: `-D clippy::same-item-push` implied by `-D warnings`
- = help: try using vec![vec![b' '];SIZE] or spaces.resize(NEW_SIZE, vec![b' '])
+ = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item)
error: it looks like the same item is being pushed into this Vec
- --> $DIR/same_item_push.rs:22:9
+ --> $DIR/same_item_push.rs:29:9
|
-LL | vec2.push(item);
- | ^^^^
+LL | vec.push(item);
+ | ^^^
|
- = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item)
+ = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item)
error: it looks like the same item is being pushed into this Vec
- --> $DIR/same_item_push.rs:28:9
+ --> $DIR/same_item_push.rs:34:9
|
-LL | vec3.push(item);
- | ^^^^
+LL | vec.push(13);
+ | ^^^
|
- = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item)
+ = help: try using vec![13;SIZE] or vec.resize(NEW_SIZE, 13)
error: it looks like the same item is being pushed into this Vec
- --> $DIR/same_item_push.rs:33:9
+ --> $DIR/same_item_push.rs:39:9
|
-LL | vec4.push(13);
- | ^^^^
+LL | vec.push(VALUE);
+ | ^^^
|
- = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13)
+ = help: try using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE)
-error: aborting due to 4 previous errors
+error: it looks like the same item is being pushed into this Vec
+ --> $DIR/same_item_push.rs:45:9
+ |
+LL | vec.push(item);
+ | ^^^
+ |
+ = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item)
+
+error: aborting due to 5 previous errors
ArrayStruct { array: [0] }.array[0] = 1;
(0, 0).0 = 1;
- A.0 = 2;
- B.field = 2;
- C.structure.field = 2;
- D.array[0] = 2;
-
// no error
s.field = 1;
t.0 = 1;
LL | (0, 0).0 = 1;
| ^^^^^^^^^^^^
-error: assignment to temporary
- --> $DIR/temporary_assignment.rs:56:5
- |
-LL | A.0 = 2;
- | ^^^^^^^
-
-error: assignment to temporary
- --> $DIR/temporary_assignment.rs:57:5
- |
-LL | B.field = 2;
- | ^^^^^^^^^^^
-
-error: assignment to temporary
- --> $DIR/temporary_assignment.rs:58:5
- |
-LL | C.structure.field = 2;
- | ^^^^^^^^^^^^^^^^^^^^^
-
-error: assignment to temporary
- --> $DIR/temporary_assignment.rs:59:5
- |
-LL | D.array[0] = 2;
- | ^^^^^^^^^^^^^^
-
-error: aborting due to 8 previous errors
+error: aborting due to 4 previous errors
let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) };
}
+// dereferencing raw pointers in const contexts, should not lint as it's unstable (issue 5959)
+const _: &() = {
+ struct ZST;
+ let zst = &ZST;
+
+ unsafe { std::mem::transmute::<&'static ZST, &'static ()>(zst) }
+};
+
fn main() {}
#![warn(clippy::unit_arg)]
-#![allow(clippy::no_effect, unused_must_use, unused_variables)]
+#![allow(
+ clippy::no_effect,
+ unused_must_use,
+ unused_variables,
+ clippy::unused_unit,
+ clippy::or_fun_call
+)]
use std::fmt::Debug;
foo(3);
},
);
+ // here Some(foo(2)) isn't the top level statement expression, wrap the suggestion in a block
+ None.or(Some(foo(2)));
+ // in this case, the suggestion can be inlined, no need for a surrounding block
+ // foo(()); foo(()) instead of { foo(()); foo(()) }
+ foo(foo(()))
}
fn ok() {
error: passing a unit value to a function
- --> $DIR/unit_arg.rs:23:5
+ --> $DIR/unit_arg.rs:29:5
|
LL | / foo({
LL | | 1;
|
LL | 1
|
-help: or move the expression in front of the call...
+help: or move the expression in front of the call and replace it with the unit literal `()`
|
LL | {
LL | 1;
LL | };
- |
-help: ...and use a unit literal instead
- |
LL | foo(());
- | ^^
+ |
error: passing a unit value to a function
- --> $DIR/unit_arg.rs:26:5
+ --> $DIR/unit_arg.rs:32:5
|
LL | foo(foo(1));
| ^^^^^^^^^^^
|
-help: move the expression in front of the call...
+help: move the expression in front of the call and replace it with the unit literal `()`
|
LL | foo(1);
- |
-help: ...and use a unit literal instead
- |
LL | foo(());
- | ^^
+ |
error: passing a unit value to a function
- --> $DIR/unit_arg.rs:27:5
+ --> $DIR/unit_arg.rs:33:5
|
LL | / foo({
LL | | foo(1);
|
LL | foo(2)
|
-help: or move the expression in front of the call...
+help: or move the expression in front of the call and replace it with the unit literal `()`
|
LL | {
LL | foo(1);
LL | foo(2);
LL | };
- |
-help: ...and use a unit literal instead
- |
LL | foo(());
- | ^^
+ |
error: passing a unit value to a function
- --> $DIR/unit_arg.rs:32:5
+ --> $DIR/unit_arg.rs:38:5
|
LL | / b.bar({
LL | | 1;
|
LL | 1
|
-help: or move the expression in front of the call...
+help: or move the expression in front of the call and replace it with the unit literal `()`
|
LL | {
LL | 1;
LL | };
- |
-help: ...and use a unit literal instead
- |
LL | b.bar(());
- | ^^
+ |
error: passing unit values to a function
- --> $DIR/unit_arg.rs:35:5
+ --> $DIR/unit_arg.rs:41:5
|
LL | taking_multiple_units(foo(0), foo(1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: move the expressions in front of the call...
+help: move the expressions in front of the call and replace them with the unit literal `()`
|
LL | foo(0);
LL | foo(1);
- |
-help: ...and use unit literals instead
- |
LL | taking_multiple_units((), ());
- | ^^ ^^
+ |
error: passing unit values to a function
- --> $DIR/unit_arg.rs:36:5
+ --> $DIR/unit_arg.rs:42:5
|
LL | / taking_multiple_units(foo(0), {
LL | | foo(1);
|
LL | foo(2)
|
-help: or move the expressions in front of the call...
+help: or move the expressions in front of the call and replace them with the unit literal `()`
|
LL | foo(0);
LL | {
LL | foo(1);
LL | foo(2);
LL | };
- |
-help: ...and use unit literals instead
- |
LL | taking_multiple_units((), ());
- | ^^ ^^
+ |
error: passing unit values to a function
- --> $DIR/unit_arg.rs:40:5
+ --> $DIR/unit_arg.rs:46:5
|
LL | / taking_multiple_units(
LL | | {
|
LL | foo(3)
|
-help: or move the expressions in front of the call...
+help: or move the expressions in front of the call and replace them with the unit literal `()`
|
LL | {
LL | foo(0);
LL | {
LL | foo(2);
...
-help: ...and use unit literals instead
+
+error: passing a unit value to a function
+ --> $DIR/unit_arg.rs:57:13
+ |
+LL | None.or(Some(foo(2)));
+ | ^^^^^^^^^^^^
|
-LL | (),
-LL | (),
+help: move the expression in front of the call and replace it with the unit literal `()`
+ |
+LL | None.or({
+LL | foo(2);
+LL | Some(())
+LL | });
|
error: passing a unit value to a function
- --> $DIR/unit_arg.rs:82:5
+ --> $DIR/unit_arg.rs:60:5
|
-LL | Some(foo(1))
+LL | foo(foo(()))
| ^^^^^^^^^^^^
|
-help: move the expression in front of the call...
+help: move the expression in front of the call and replace it with the unit literal `()`
|
-LL | foo(1);
+LL | foo(());
+LL | foo(())
|
-help: ...and use a unit literal instead
+
+error: passing a unit value to a function
+ --> $DIR/unit_arg.rs:93:5
|
+LL | Some(foo(1))
+ | ^^^^^^^^^^^^
+ |
+help: move the expression in front of the call and replace it with the unit literal `()`
+ |
+LL | foo(1);
LL | Some(())
- | ^^
+ |
-error: aborting due to 8 previous errors
+error: aborting due to 10 previous errors
LL | taking_two_units({}, foo(0));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: move the expression in front of the call...
+help: move the expression in front of the call and replace it with the unit literal `()`
|
LL | foo(0);
- |
-help: ...and use unit literals instead
- |
LL | taking_two_units((), ());
- | ^^ ^^
+ |
error: passing unit values to a function
--> $DIR/unit_arg_empty_blocks.rs:18:5
LL | taking_three_units({}, foo(0), foo(1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: move the expressions in front of the call...
+help: move the expressions in front of the call and replace them with the unit literal `()`
|
LL | foo(0);
LL | foo(1);
- |
-help: ...and use unit literals instead
- |
LL | taking_three_units((), (), ());
- | ^^ ^^ ^^
+ |
error: aborting due to 4 previous errors
vec.sort_by(|_, b| b.cmp(&5));
vec.sort_by(|_, b| b.cmp(c));
vec.sort_unstable_by(|a, _| a.cmp(c));
+
+ // Ignore vectors of references
+ let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5];
+ vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs()));
+ vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs()));
+ vec.sort_by(|a, b| b.cmp(a));
+ vec.sort_unstable_by(|a, b| b.cmp(a));
}
-// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162
+// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key`
mod issue_5754 {
- struct Test(String);
+ #[derive(Clone, Copy)]
+ struct Test(usize);
#[derive(PartialOrd, Ord, PartialEq, Eq)]
- struct Wrapper<'a>(&'a str);
+ struct Wrapper<'a>(&'a usize);
impl Test {
- fn name(&self) -> &str {
+ fn name(&self) -> &usize {
&self.0
}
}
}
+// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K`
+// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are
+// not linted.
+mod issue_6001 {
+ struct Test(String);
+
+ impl Test {
+ // Return an owned type so that we don't hit the fix for 5754
+ fn name(&self) -> String {
+ self.0.clone()
+ }
+ }
+
+ pub fn test() {
+ let mut args: Vec<Test> = vec![];
+
+ // Forward
+ args.sort_by(|a, b| a.name().cmp(&b.name()));
+ args.sort_unstable_by(|a, b| a.name().cmp(&b.name()));
+ // Reverse
+ args.sort_by(|a, b| b.name().cmp(&a.name()));
+ args.sort_unstable_by(|a, b| b.name().cmp(&a.name()));
+ }
+}
+
fn main() {
unnecessary_sort_by();
issue_5754::test();
+ issue_6001::test();
}
vec.sort_by(|_, b| b.cmp(&5));
vec.sort_by(|_, b| b.cmp(c));
vec.sort_unstable_by(|a, _| a.cmp(c));
+
+ // Ignore vectors of references
+ let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5];
+ vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs()));
+ vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs()));
+ vec.sort_by(|a, b| b.cmp(a));
+ vec.sort_unstable_by(|a, b| b.cmp(a));
}
-// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162
+// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key`
mod issue_5754 {
- struct Test(String);
+ #[derive(Clone, Copy)]
+ struct Test(usize);
#[derive(PartialOrd, Ord, PartialEq, Eq)]
- struct Wrapper<'a>(&'a str);
+ struct Wrapper<'a>(&'a usize);
impl Test {
- fn name(&self) -> &str {
+ fn name(&self) -> &usize {
&self.0
}
}
}
+// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K`
+// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are
+// not linted.
+mod issue_6001 {
+ struct Test(String);
+
+ impl Test {
+ // Return an owned type so that we don't hit the fix for 5754
+ fn name(&self) -> String {
+ self.0.clone()
+ }
+ }
+
+ pub fn test() {
+ let mut args: Vec<Test> = vec![];
+
+ // Forward
+ args.sort_by(|a, b| a.name().cmp(&b.name()));
+ args.sort_unstable_by(|a, b| a.name().cmp(&b.name()));
+ // Reverse
+ args.sort_by(|a, b| b.name().cmp(&a.name()));
+ args.sort_unstable_by(|a, b| b.name().cmp(&a.name()));
+ }
+}
+
fn main() {
unnecessary_sort_by();
issue_5754::test();
+ issue_6001::test();
}
pub use self::b::C;
}
+// don't lint on clippy::wildcard_imports for `use` items
+#[allow(clippy::wildcard_imports)]
+pub use std::io::prelude::*;
+
+// don't lint on clippy::enum_glob_use for `use` items
+#[allow(clippy::enum_glob_use)]
+pub use std::cmp::Ordering::*;
+
fn test_indented_attr() {
#![allow(clippy::almost_swapped)]
use std::collections::HashSet;
pub use self::b::C;
}
+// don't lint on clippy::wildcard_imports for `use` items
+#[allow(clippy::wildcard_imports)]
+pub use std::io::prelude::*;
+
+// don't lint on clippy::enum_glob_use for `use` items
+#[allow(clippy::enum_glob_use)]
+pub use std::cmp::Ordering::*;
+
fn test_indented_attr() {
#[allow(clippy::almost_swapped)]
use std::collections::HashSet;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)`
error: useless lint attribute
- --> $DIR/useless_attribute.rs:53:5
+ --> $DIR/useless_attribute.rs:61:5
|
LL | #[allow(clippy::almost_swapped)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]`