[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
+[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
+[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
+[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
-[There are 321 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+[There are 324 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
}
impl Lint {
+ #[must_use]
pub fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
Self {
name: name.to_lowercase(),
}
/// Returns the lints in a `HashMap`, grouped by the different lint groups
+ #[must_use]
pub fn by_lint_group(lints: &[Self]) -> HashMap<String, Vec<Self>> {
lints
.iter()
.into_group_map()
}
+ #[must_use]
pub fn is_internal(&self) -> bool {
self.group.starts_with("internal")
}
}
/// Generates the Vec items for `register_lint_group` calls in `clippy_lints/src/lib.rs`.
+#[must_use]
pub fn gen_lint_group_list(lints: Vec<Lint>) -> Vec<String> {
lints
.into_iter()
}
/// Generates the `pub mod module_name` list in `clippy_lints/src/lib.rs`.
+#[must_use]
pub fn gen_modules_list(lints: Vec<Lint>) -> Vec<String> {
lints
.into_iter()
}
/// Generates the list of lint links at the bottom of the README
+#[must_use]
pub fn gen_changelog_lint_list(lints: Vec<Lint>) -> Vec<String> {
let mut lint_list_sorted: Vec<Lint> = lints;
lint_list_sorted.sort_by_key(|l| l.name.clone());
}
/// Generates the `register_removed` code in `./clippy_lints/src/lib.rs`.
+#[must_use]
pub fn gen_deprecated(lints: &[Lint]) -> Vec<String> {
lints
.iter()
.filter(|f| f.path().extension() == Some(OsStr::new("stderr")))
}
+#[must_use]
fn count_linenumbers(filepath: &str) -> usize {
if let Ok(mut file) = File::open(filepath) {
let mut content = String::new();
/// Returns `false` if the number of significant figures in `value` are
/// less than `min_digits`; otherwise, returns true if `value` is equal
/// to `constant`, rounded to the number of digits present in `value`.
+#[must_use]
fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
if value.len() <= min_digits {
false
);
}
+#[must_use]
fn is_commutative(op: hir::BinOpKind) -> bool {
use rustc::hir::BinOpKind::*;
match op {
}
impl BitMask {
+ #[must_use]
pub fn new(verbose_bit_mask_threshold: u64) -> Self {
Self {
verbose_bit_mask_threshold,
}
}
+#[must_use]
fn invert_cmp(cmp: BinOpKind) -> BinOpKind {
match cmp {
BinOpKind::Eq => BinOpKind::Eq,
impl ConversionType {
/// Creates a conversion type if the type is allowed & conversion is valid
+ #[must_use]
fn try_new(from: &str, to: &str) -> Option<Self> {
if UINTS.contains(&from) {
Some(Self::FromUnsigned)
}
impl CognitiveComplexity {
+ #[must_use]
pub fn new(limit: u64) -> Self {
Self {
limit: LimitStack::new(limit),
/// need to keep track of
/// the spans but this function is inspired from the later.
#[allow(clippy::cast_possible_truncation)]
+#[must_use]
pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(usize, Span)>) {
// one-line comments lose their prefix
const ONELINERS: &[&str] = &["///!", "///", "//!", "//"];
}
impl EnumVariantNames {
+ #[must_use]
pub fn new(threshold: u64) -> Self {
Self {
modules: Vec::new(),
]);
/// Returns the number of chars that match from the start
+#[must_use]
fn partial_match(pre: &str, name: &str) -> usize {
let mut name_iter = name.chars();
let _ = name_iter.next_back(); // make sure the name is never fully matched
}
/// Returns the number of chars that match from the end
+#[must_use]
fn partial_rmatch(post: &str, name: &str) -> usize {
let mut name_iter = name.chars();
let _ = name_iter.next(); // make sure the name is never fully matched
);
}
+#[must_use]
fn to_camel_case(item_name: &str) -> String {
let mut s = String::new();
let mut up = true;
impl ExcessivePrecision {
// None if nothing to lint, Some(suggestion) if lint necessary
+ #[must_use]
fn check(self, sym: Symbol, fty: FloatTy) -> Option<String> {
let max = max_digits(fty);
let sym_str = sym.as_str();
/// Should we exclude the float because it has a `.0` or `.` suffix
/// Ex `1_000_000_000.0`
/// Ex `1_000_000_000.`
+#[must_use]
fn dot_zero_exclusion(s: &str) -> bool {
s.split('.').nth(1).map_or(false, |after_dec| {
let mut decpart = after_dec.chars().take_while(|c| *c != 'e' || *c != 'E');
})
}
+#[must_use]
fn max_digits(fty: FloatTy) -> u32 {
match fty {
FloatTy::F32 => f32::DIGITS,
}
/// Counts the digits excluding leading zeros
+#[must_use]
fn count_digits(s: &str) -> usize {
// Note that s does not contain the f32/64 suffix, and underscores have been stripped
s.chars()
Normal,
}
impl FloatFormat {
+ #[must_use]
fn new(s: &str) -> Self {
s.chars()
.find_map(|x| match x {
}
}
+#[must_use]
fn has_unary_equivalent(bin_op: BinOpKind) -> bool {
// &, *, -
bin_op == BinOpKind::And || bin_op == BinOpKind::Mul || bin_op == BinOpKind::Sub
-use std::convert::TryFrom;
-
-use crate::utils::{iter_input_pats, qpath_res, snippet, snippet_opt, span_lint, type_is_unsafe_function};
+use crate::utils::{
+ iter_input_pats, match_def_path, qpath_res, return_ty, snippet, snippet_opt, span_help_and_lint, span_lint,
+ span_lint_and_then, type_is_unsafe_function,
+};
use matches::matches;
-use rustc::hir;
-use rustc::hir::def::Res;
-use rustc::hir::intravisit;
+use rustc::hir::{self, def::Res, def_id::DefId, intravisit};
use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintContext, LintPass};
-use rustc::ty;
+use rustc::ty::{self, Ty};
use rustc::{declare_tool_lint, impl_lint_pass};
use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::Applicability;
use rustc_target::spec::abi::Abi;
-use syntax::source_map::{BytePos, Span};
+use syntax::ast::Attribute;
+use syntax::source_map::Span;
declare_clippy_lint! {
/// **What it does:** Checks for functions with too many parameters.
"public functions dereferencing raw pointer arguments but not marked `unsafe`"
}
+declare_clippy_lint! {
+ /// **What it does:** Checks for a [`#[must_use]`] attribute on
+ /// unit-returning functions and methods.
+ ///
+ /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
+ ///
+ /// **Why is this bad?** Unit values are useless. The attribute is likely
+ /// a remnant of a refactoring that removed the return type.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Examples:**
+ /// ```rust
+ /// #[must_use]
+ /// fn useless() { }
+ /// ```
+ pub MUST_USE_UNIT,
+ style,
+ "`#[must_use]` attribute on a unit-returning function / method"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for a [`#[must_use]`] attribute without
+ /// further information on functions and methods that return a type already
+ /// marked as `#[must_use]`.
+ ///
+ /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
+ ///
+ /// **Why is this bad?** The attribute isn't needed. Not using the result
+ /// will already be reported. Alternatively, one can add some text to the
+ /// attribute to improve the lint message.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Examples:**
+ /// ```rust
+ /// #[must_use]
+ /// fn double_must_use() -> Result<(), ()> {
+ /// unimplemented!();
+ /// }
+ /// ```
+ pub DOUBLE_MUST_USE,
+ style,
+ "`#[must_use]` attribute on a `#[must_use]`-returning function / method"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for public functions that have no
+ /// [`#[must_use]`] attribute, but return something not already marked
+ /// must-use, have no mutable arg and mutate no statics.
+ ///
+ /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
+ ///
+ /// **Why is this bad?** Not bad at all, this lint just shows places where
+ /// you could add the attribute.
+ ///
+ /// **Known problems:** The lint only checks the arguments for mutable
+ /// types without looking if they are actually changed. On the other hand,
+ /// it also ignores a broad range of potentially interesting side effects,
+ /// because we cannot decide whether the programmer intends the function to
+ /// be called for the side effect or the result. Expect many false
+ /// positives. At least we don't lint if the result type is unit or already
+ /// `#[must_use]`.
+ ///
+ /// **Examples:**
+ /// ```rust
+ /// // this could be annotated with `#[must_use]`.
+ /// fn id<T>(t: T) -> T { t }
+ /// ```
+ pub MUST_USE_CANDIDATE,
+ pedantic,
+ "function or method that could take a `#[must_use]` attribute"
+}
+
#[derive(Copy, Clone)]
pub struct Functions {
threshold: u64,
}
}
-impl_lint_pass!(Functions => [TOO_MANY_ARGUMENTS, TOO_MANY_LINES, NOT_UNSAFE_PTR_ARG_DEREF]);
+impl_lint_pass!(Functions => [
+ TOO_MANY_ARGUMENTS,
+ TOO_MANY_LINES,
+ NOT_UNSAFE_PTR_ARG_DEREF,
+ MUST_USE_UNIT,
+ DOUBLE_MUST_USE,
+ MUST_USE_CANDIDATE,
+]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions {
fn check_fn(
_,
)
| hir::intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _, _) => {
- self.check_arg_number(cx, decl, span)
+ self.check_arg_number(cx, decl, span.with_hi(decl.output.span().hi()))
},
_ => {},
}
self.check_line_number(cx, span, body);
}
+ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) {
+ let attr = must_use_attr(&item.attrs);
+ if let hir::ItemKind::Fn(ref decl, ref _header, ref _generics, ref body_id) = item.kind {
+ if let Some(attr) = attr {
+ let fn_header_span = item.span.with_hi(decl.output.span().hi());
+ check_needless_must_use(cx, decl, item.hir_id, item.span, fn_header_span, attr);
+ return;
+ }
+ if cx.access_levels.is_exported(item.hir_id) {
+ check_must_use_candidate(
+ cx,
+ decl,
+ cx.tcx.hir().body(*body_id),
+ item.span,
+ item.hir_id,
+ item.span.with_hi(decl.output.span().hi()),
+ "this function could have a `#[must_use]` attribute",
+ );
+ }
+ }
+ }
+
+ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem) {
+ if let hir::ImplItemKind::Method(ref sig, ref body_id) = item.kind {
+ let attr = must_use_attr(&item.attrs);
+ if let Some(attr) = attr {
+ let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+ check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr);
+ } else if cx.access_levels.is_exported(item.hir_id) {
+ check_must_use_candidate(
+ cx,
+ &sig.decl,
+ cx.tcx.hir().body(*body_id),
+ item.span,
+ item.hir_id,
+ item.span.with_hi(sig.decl.output.span().hi()),
+ "this method could have a `#[must_use]` attribute",
+ );
+ }
+ }
+ }
+
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem) {
if let hir::TraitItemKind::Method(ref sig, ref eid) = item.kind {
// don't lint extern functions decls, it's not their fault
if sig.header.abi == Abi::Rust {
- self.check_arg_number(cx, &sig.decl, item.span);
+ self.check_arg_number(cx, &sig.decl, item.span.with_hi(sig.decl.output.span().hi()));
}
+ let attr = must_use_attr(&item.attrs);
+ if let Some(attr) = attr {
+ let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+ check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr);
+ }
if let hir::TraitMethod::Provided(eid) = *eid {
let body = cx.tcx.hir().body(eid);
self.check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id);
+
+ if attr.is_none() && cx.access_levels.is_exported(item.hir_id) {
+ check_must_use_candidate(
+ cx,
+ &sig.decl,
+ body,
+ item.span,
+ item.hir_id,
+ item.span.with_hi(sig.decl.output.span().hi()),
+ "this method could have a `#[must_use]` attribute",
+ );
+ }
}
}
}
}
impl<'a, 'tcx> Functions {
- fn check_arg_number(self, cx: &LateContext<'_, '_>, decl: &hir::FnDecl, span: Span) {
- // Remove the function body from the span. We can't use `SourceMap::def_span` because the
- // argument list might span multiple lines.
- let span = if let Some(snippet) = snippet_opt(cx, span) {
- let snippet = snippet.split('{').nth(0).unwrap_or("").trim_end();
- if snippet.is_empty() {
- span
- } else {
- span.with_hi(BytePos(span.lo().0 + u32::try_from(snippet.len()).unwrap()))
- }
- } else {
- span
- };
-
+ fn check_arg_number(self, cx: &LateContext<'_, '_>, decl: &hir::FnDecl, fn_span: Span) {
let args = decl.inputs.len() as u64;
if args > self.threshold {
span_lint(
cx,
TOO_MANY_ARGUMENTS,
- span,
+ fn_span,
&format!("this function has too many arguments ({}/{})", args, self.threshold),
);
}
}
}
+fn check_needless_must_use(
+ cx: &LateContext<'_, '_>,
+ decl: &hir::FnDecl,
+ item_id: hir::HirId,
+ item_span: Span,
+ fn_header_span: Span,
+ attr: &Attribute,
+) {
+ if in_external_macro(cx.sess(), item_span) {
+ return;
+ }
+ if returns_unit(decl) {
+ span_lint_and_then(
+ cx,
+ MUST_USE_UNIT,
+ fn_header_span,
+ "this unit-returning function has a `#[must_use]` attribute",
+ |db| {
+ db.span_suggestion(
+ attr.span,
+ "remove the attribute",
+ "".into(),
+ Applicability::MachineApplicable,
+ );
+ },
+ );
+ } else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) {
+ span_help_and_lint(
+ cx,
+ DOUBLE_MUST_USE,
+ fn_header_span,
+ "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
+ "either add some descriptive text or remove the attribute",
+ );
+ }
+}
+
+fn check_must_use_candidate<'a, 'tcx>(
+ cx: &LateContext<'a, 'tcx>,
+ decl: &'tcx hir::FnDecl,
+ body: &'tcx hir::Body,
+ item_span: Span,
+ item_id: hir::HirId,
+ fn_span: Span,
+ msg: &str,
+) {
+ if has_mutable_arg(cx, body)
+ || mutates_static(cx, body)
+ || in_external_macro(cx.sess(), item_span)
+ || returns_unit(decl)
+ || is_must_use_ty(cx, return_ty(cx, item_id))
+ {
+ return;
+ }
+ span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |db| {
+ if let Some(snippet) = snippet_opt(cx, fn_span) {
+ db.span_suggestion(
+ fn_span,
+ "add the attribute",
+ format!("#[must_use] {}", snippet),
+ Applicability::MachineApplicable,
+ );
+ }
+ });
+}
+
+fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
+ attrs
+ .iter()
+ .find(|attr| attr.ident().map_or(false, |ident| "must_use" == &ident.as_str()))
+}
+
+fn returns_unit(decl: &hir::FnDecl) -> bool {
+ match decl.output {
+ hir::FunctionRetTy::DefaultReturn(_) => true,
+ hir::FunctionRetTy::Return(ref ty) => match ty.kind {
+ hir::TyKind::Tup(ref tys) => tys.is_empty(),
+ hir::TyKind::Never => true,
+ _ => false,
+ },
+ }
+}
+
+fn is_must_use_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
+ use ty::TyKind::*;
+ match ty.kind {
+ Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(),
+ Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(),
+ Slice(ref ty) | Array(ref ty, _) | RawPtr(ty::TypeAndMut { ref ty, .. }) | Ref(_, ref ty, _) => {
+ // for the Array case we don't need to care for the len == 0 case
+ // because we don't want to lint functions returning empty arrays
+ is_must_use_ty(cx, *ty)
+ },
+ Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
+ Opaque(ref def_id, _) => {
+ for (predicate, _) in &cx.tcx.predicates_of(*def_id).predicates {
+ if let ty::Predicate::Trait(ref poly_trait_predicate) = predicate {
+ if must_use_attr(&cx.tcx.get_attrs(poly_trait_predicate.skip_binder().trait_ref.def_id)).is_some() {
+ return true;
+ }
+ }
+ }
+ false
+ },
+ Dynamic(binder, _) => {
+ for predicate in binder.skip_binder().iter() {
+ if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate {
+ if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
+ return true;
+ }
+ }
+ }
+ false
+ },
+ _ => false,
+ }
+}
+
+fn has_mutable_arg(cx: &LateContext<'_, '_>, body: &hir::Body) -> bool {
+ let mut tys = FxHashSet::default();
+ body.params.iter().any(|param| is_mutable_pat(cx, ¶m.pat, &mut tys))
+}
+
+fn is_mutable_pat(cx: &LateContext<'_, '_>, pat: &hir::Pat, tys: &mut FxHashSet<DefId>) -> bool {
+ if let hir::PatKind::Wild = pat.kind {
+ return false; // ignore `_` patterns
+ }
+ let def_id = pat.hir_id.owner_def_id();
+ if cx.tcx.has_typeck_tables(def_id) {
+ is_mutable_ty(cx, &cx.tcx.typeck_tables_of(def_id).pat_ty(pat), pat.span, tys)
+ } else {
+ false
+ }
+}
+
+static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]];
+
+fn is_mutable_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet<DefId>) -> bool {
+ use ty::TyKind::*;
+ match ty.kind {
+ // primitive types are never mutable
+ Bool | Char | Int(_) | Uint(_) | Float(_) | Str => false,
+ Adt(ref adt, ref substs) => {
+ tys.insert(adt.did) && !ty.is_freeze(cx.tcx, cx.param_env, span)
+ || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path))
+ && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys))
+ },
+ Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)),
+ Array(ty, _) | Slice(ty) => is_mutable_ty(cx, ty, span, tys),
+ RawPtr(ty::TypeAndMut { ty, mutbl }) | Ref(_, ty, mutbl) => {
+ mutbl == hir::Mutability::MutMutable || is_mutable_ty(cx, ty, span, tys)
+ },
+ // calling something constitutes a side effect, so return true on all callables
+ // also never calls need not be used, so return true for them, too
+ _ => true,
+ }
+}
+
fn raw_ptr_arg(arg: &hir::Param, ty: &hir::Ty) -> Option<hir::HirId> {
if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) {
Some(id)
tables: &'a ty::TypeckTables<'tcx>,
}
-impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
match expr.kind {
hir::ExprKind::Call(ref f, ref args) => {
_ => (),
}
- hir::intravisit::walk_expr(self, expr);
+ intravisit::walk_expr(self, expr);
}
+
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
intravisit::NestedVisitorMap::None
}
}
}
}
+
+struct StaticMutVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'a, 'tcx>,
+ mutates_static: bool,
+}
+
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
+ fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
+ use hir::ExprKind::*;
+
+ if self.mutates_static {
+ return;
+ }
+ match expr.kind {
+ Call(_, ref args) | MethodCall(_, _, ref args) => {
+ let mut tys = FxHashSet::default();
+ for arg in args {
+ let def_id = arg.hir_id.owner_def_id();
+ if self.cx.tcx.has_typeck_tables(def_id)
+ && is_mutable_ty(
+ self.cx,
+ self.cx.tcx.typeck_tables_of(def_id).expr_ty(arg),
+ arg.span,
+ &mut tys,
+ )
+ && is_mutated_static(self.cx, arg)
+ {
+ self.mutates_static = true;
+ return;
+ }
+ tys.clear();
+ }
+ },
+ Assign(ref target, _) | AssignOp(_, ref target, _) | AddrOf(hir::Mutability::MutMutable, ref target) => {
+ self.mutates_static |= is_mutated_static(self.cx, target)
+ },
+ _ => {},
+ }
+ }
+
+ fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
+ intravisit::NestedVisitorMap::None
+ }
+}
+
+fn is_mutated_static(cx: &LateContext<'_, '_>, e: &hir::Expr) -> bool {
+ use hir::ExprKind::*;
+
+ match e.kind {
+ Path(ref qpath) => {
+ if let Res::Local(_) = qpath_res(cx, qpath, e.hir_id) {
+ false
+ } else {
+ true
+ }
+ },
+ Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner),
+ _ => false,
+ }
+}
+
+fn mutates_static<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, body: &'tcx hir::Body) -> bool {
+ let mut v = StaticMutVisitor {
+ cx,
+ mutates_static: false,
+ };
+ intravisit::walk_expr(&mut v, &body.value);
+ v.mutates_static
+}
use self::Finiteness::{Finite, Infinite, MaybeInfinite};
impl Finiteness {
+ #[must_use]
fn and(self, b: Self) -> Self {
match (self, b) {
(Finite, _) | (_, Finite) => Finite,
}
}
+ #[must_use]
fn or(self, b: Self) -> Self {
match (self, b) {
(Infinite, _) | (_, Infinite) => Infinite,
}
impl From<bool> for Finiteness {
+ #[must_use]
fn from(b: bool) -> Self {
if b {
Infinite
}
impl LargeEnumVariant {
+ #[must_use]
pub fn new(maximum_size_difference_allowed: u64) -> Self {
Self {
maximum_size_difference_allowed,
#![feature(rustc_private)]
#![feature(slice_patterns)]
#![feature(stmt_expr_attributes)]
-#![allow(clippy::missing_docs_in_private_items)]
+#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
#![recursion_limit = "512"]
#![warn(rust_2018_idioms, trivial_casts, trivial_numeric_casts)]
#![deny(rustc::internal)]
enum_variants::MODULE_NAME_REPETITIONS,
enum_variants::PUB_ENUM_VARIANT_NAMES,
eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
+ functions::MUST_USE_CANDIDATE,
functions::TOO_MANY_LINES,
if_not_else::IF_NOT_ELSE,
infinite_iter::MAYBE_INFINITE_ITER,
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
formatting::SUSPICIOUS_ELSE_FORMATTING,
formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
+ functions::DOUBLE_MUST_USE,
+ functions::MUST_USE_UNIT,
functions::NOT_UNSAFE_PTR_ARG_DEREF,
functions::TOO_MANY_ARGUMENTS,
get_last_with_len::GET_LAST_WITH_LEN,
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
formatting::SUSPICIOUS_ELSE_FORMATTING,
formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
+ functions::DOUBLE_MUST_USE,
+ functions::MUST_USE_UNIT,
infallible_destructuring_match::INFALLIBLE_DESTRUCTURING_MATCH,
inherent_to_string::INHERENT_TO_STRING,
len_zero::LEN_WITHOUT_IS_EMPTY,
}
/// Number of unique lifetimes in the given vector.
+#[must_use]
fn unique_lifetimes(lts: &[RefLt]) -> usize {
lts.iter().collect::<FxHashSet<_>>().len()
}
impl Radix {
/// Returns a reasonable digit group size for this radix.
+ #[must_use]
crate fn suggest_grouping(&self) -> usize {
match *self {
Self::Binary | Self::Hexadecimal => 4,
}
impl<'a> DigitInfo<'a> {
+ #[must_use]
crate fn new(lit: &'a str, float: bool) -> Self {
// Determine delimiter for radix prefix, if present, and radix.
let radix = if lit.starts_with("0x") {
/// Given the sizes of the digit groups of both integral and fractional
/// parts, and the length
/// of both parts, determine if the digits have been grouped consistently.
+ #[must_use]
fn parts_consistent(int_group_size: usize, frac_group_size: usize, int_size: usize, frac_size: usize) -> bool {
match (int_group_size, frac_group_size) {
// No groups on either side of decimal point - trivially consistent.
}
impl DecimalLiteralRepresentation {
+ #[must_use]
pub fn new(threshold: u64) -> Self {
Self { threshold }
}
}
}
+#[must_use]
fn is_mistyped_suffix(suffix: &str) -> bool {
["_8", "_16", "_32", "_64"].contains(&suffix)
}
+#[must_use]
fn is_possible_suffix_index(lit: &str, idx: usize, len: usize) -> bool {
((len > 3 && idx == len - 3) || (len > 2 && idx == len - 2)) && is_mistyped_suffix(lit.split_at(idx).1)
}
+#[must_use]
fn is_mistyped_float_suffix(suffix: &str) -> bool {
["_32", "_64"].contains(&suffix)
}
+#[must_use]
fn is_possible_float_suffix_index(lit: &str, idx: usize, len: usize) -> bool {
(len > 3 && idx == len - 3) && is_mistyped_float_suffix(lit.split_at(idx).1)
}
+#[must_use]
fn has_possible_float_suffix(lit: &str) -> bool {
lit.ends_with("_32") || lit.ends_with("_64")
}
Otherwise,
}
+#[must_use]
fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult {
match *arg {
NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
}
// Combine two results for parts that are called in order.
+#[must_use]
fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult {
match first {
NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first,
}
// Combine two results where both parts are called but not necessarily in order.
+#[must_use]
fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult {
match (left, right) {
(NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
}
// Combine two results where only one of the part may have been executed.
+#[must_use]
fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult {
match (b1, b2) {
(NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
}
}
+#[must_use]
fn suggestion_msg(function_type: &str, map_type: &str) -> String {
format!(
"called `map(f)` on an {0} value where `f` is a unit {1}",
}
}
+ #[must_use]
fn description(self) -> &'static str {
match self {
Self::Value => "self by value",
}
impl Convention {
+ #[must_use]
fn check(&self, other: &str) -> bool {
match *self {
Self::Eq(this) => this == other,
}
impl ReturnVisitor {
+ #[must_use]
fn new() -> Self {
Self { found_return: false }
}
}
// We don't have to lint on something that's already `const`
+#[must_use]
fn already_const(header: hir::FnHeader) -> bool {
header.constness == Constness::Const
}
}
impl ::std::default::Default for MissingDoc {
+ #[must_use]
fn default() -> Self {
Self::new()
}
}
impl MissingDoc {
+ #[must_use]
pub fn new() -> Self {
Self {
doc_hidden_stack: vec![false],
///
/// NOTE: when there is no closing brace in `s`, `s` is _not_ preserved, i.e.,
/// an empty string will be returned in that case.
+#[must_use]
pub fn erode_from_back(s: &str) -> String {
let mut ret = String::from(s);
while ret.pop().map_or(false, |c| c != '}') {}
/// inside_a_block();
/// }
/// ```
+#[must_use]
pub fn erode_from_front(s: &str) -> String {
s.chars()
.skip_while(|c| c.is_whitespace())
/// tries to get the contents of the block. If there is no closing brace
/// present,
/// an empty string is returned.
+#[must_use]
pub fn erode_block(s: &str) -> String {
erode_from_back(&erode_from_front(s))
}
}
impl Source {
+ #[must_use]
fn lint(&self) -> (&'static Lint, &'static str, Span) {
match self {
Self::Item { item } | Self::Assoc { item, .. } => (
}
}
+#[must_use]
fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> {
for &allow in WHITELIST {
if whitelisted(interned_name, allow) {
None
}
+#[must_use]
fn whitelisted(interned_name: &str, list: &[&str]) -> bool {
list.iter()
.any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
}
/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
+#[must_use]
fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
debug_assert!(a_name.chars().count() < b_name.chars().count());
let mut a_chars = a_name.chars();
}
}
+#[must_use]
fn is_bit_op(op: BinOpKind) -> bool {
use syntax::ast::BinOpKind::*;
match op {
}
}
+#[must_use]
fn is_arith_op(op: BinOpKind) -> bool {
use syntax::ast::BinOpKind::*;
match op {
}
impl Method {
+ #[must_use]
fn suggestion(self) -> &'static str {
match self {
Self::Offset => "add",
}
#[allow(clippy::cast_possible_truncation)] // truncation very unlikely here
+#[must_use]
fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u16) -> Span {
let offset = u32::from(offset);
let end = base.lo() + BytePos(u32::try_from(c.end.offset).expect("offset too large") + offset);
}
// get the def site
+#[must_use]
fn get_def(span: Span) -> Option<Span> {
if span.from_expansion() {
Some(span.ctxt().outer_expn_data().def_site)
}
impl TypeComplexity {
+ #[must_use]
pub fn new(threshold: u64) -> Self {
Self { threshold }
}
impl FullInt {
#[allow(clippy::cast_sign_loss)]
+ #[must_use]
fn cmp_s_u(s: i128, u: u128) -> Ordering {
if s < 0 {
Ordering::Less
}
impl PartialEq for FullInt {
+ #[must_use]
fn eq(&self, other: &Self) -> bool {
self.partial_cmp(other).expect("partial_cmp only returns Some(_)") == Ordering::Equal
}
}
impl PartialOrd for FullInt {
+ #[must_use]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(match (self, other) {
(&Self::S(s), &Self::S(o)) => s.cmp(&o),
}
}
impl Ord for FullInt {
+ #[must_use]
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other)
.expect("partial_cmp for FullInt can never return None")
}
}
+#[must_use]
fn contains_unsafe(name: &LocalInternedString) -> bool {
name.contains("Unsafe") || name.contains("unsafe")
}
}
impl LimitStack {
+ #[must_use]
pub fn new(limit: u64) -> Self {
Self { stack: vec![limit] }
}
}
impl PrintVisitor {
+ #[must_use]
fn new(s: &'static str) -> Self {
Self {
ids: FxHashMap::default(),
get_attr(sess, attrs, "author").count() > 0
}
+#[must_use]
fn desugaring_name(des: hir::MatchSource) -> String {
match des {
hir::MatchSource::ForLoopDesugar => "MatchSource::ForLoopDesugar".to_string(),
}
}
+#[must_use]
fn loop_desugaring_name(des: hir::LoopSource) -> &'static str {
match des {
hir::LoopSource::ForLoop => "LoopSource::ForLoop",
/// Returns the index of the character after the first camel-case component of `s`.
+#[must_use]
pub fn until(s: &str) -> usize {
let mut iter = s.char_indices();
if let Some((_, first)) = iter.next() {
}
/// Returns index of the last camel-case component of `s`.
+#[must_use]
pub fn from(s: &str) -> usize {
let mut iter = s.char_indices().rev();
if let Some((_, first)) = iter.next() {
}
}
+ #[must_use]
fn $rust_name() -> define_Conf!(TY $($ty)+) {
define_Conf!(DEFAULT $($ty)+, $default)
}
}
impl Default for Conf {
+ #[must_use]
fn default() -> Self {
toml::from_str("").expect("we never error on empty config files")
}
use syntax::ast;
/// Converts a hir binary operator to the corresponding `ast` type.
+#[must_use]
pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
match op {
hir::BinOpKind::Eq => ast::BinOpKind::Eq,
}
impl CompilerLintFunctions {
+ #[must_use]
pub fn new() -> Self {
let mut map = FxHashMap::default();
map.insert("span_lint", "utils::span_lint");
/// Returns `true` if the two spans come from differing expansions (i.e., one is
/// from a macro and one isn't).
+#[must_use]
pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
rhs.ctxt() != lhs.ctxt()
}
}
/// Returns `true` if this `span` was expanded by any macro.
+#[must_use]
pub fn in_macro(span: Span) -> bool {
if span.from_expansion() {
if let ExpnKind::Desugaring(..) = span.ctxt().outer_expn_data().kind {
/// Returns the pre-expansion span if is this comes from an expansion of the
/// macro `name`.
/// See also `is_direct_expn_of`.
+#[must_use]
pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
loop {
if span.from_expansion() {
/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
/// `bar!` by
/// `is_direct_expn_of`.
+#[must_use]
pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
if span.from_expansion() {
let data = span.ctxt().outer_expn_data();
/// Chained `as` and explicit `:` type coercion never need inner parenthesis so
/// they are considered
/// associative.
+#[must_use]
fn associativity(op: &AssocOp) -> Associativity {
use syntax::util::parser::AssocOp::*;
}
}
+#[must_use]
pub fn get_commit_hash() -> Option<String> {
std::process::Command::new("git")
.args(&["rev-parse", "--short", "HEAD"])
.and_then(|r| String::from_utf8(r.stdout).ok())
}
+#[must_use]
pub fn get_commit_date() -> Option<String> {
std::process::Command::new("git")
.args(&["log", "-1", "--date=short", "--pretty=format:%cd"])
.and_then(|r| String::from_utf8(r.stdout).ok())
}
+#[must_use]
pub fn get_channel() -> Option<String> {
match env::var("CFG_RELEASE_CHANNEL") {
Ok(channel) => Some(channel),
pub use lint::LINT_LEVELS;
// begin lint list, do not remove this comment, it’s used in `update_lints`
-pub const ALL_LINTS: [Lint; 321] = [
+pub const ALL_LINTS: [Lint; 324] = [
Lint {
name: "absurd_extreme_comparisons",
group: "correctness",
deprecation: None,
module: "double_comparison",
},
+ Lint {
+ name: "double_must_use",
+ group: "style",
+ desc: "`#[must_use]` attribute on a `#[must_use]`-returning function / method",
+ deprecation: None,
+ module: "functions",
+ },
Lint {
name: "double_neg",
group: "style",
deprecation: None,
module: "inherent_impl",
},
+ Lint {
+ name: "must_use_candidate",
+ group: "pedantic",
+ desc: "function or method that could take a `#[must_use]` attribute",
+ deprecation: None,
+ module: "functions",
+ },
+ Lint {
+ name: "must_use_unit",
+ group: "style",
+ desc: "`#[must_use]` attribute on a unit-returning function / method",
+ deprecation: None,
+ module: "functions",
+ },
Lint {
name: "mut_from_ref",
group: "correctness",
use std::io;
use std::path::{Path, PathBuf};
+#[must_use]
fn clippy_driver_path() -> PathBuf {
if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") {
PathBuf::from(path)
}
}
+#[must_use]
fn host_libs() -> PathBuf {
if let Some(path) = option_env!("HOST_LIBS") {
PathBuf::from(path)
}
}
+#[must_use]
fn rustc_test_suite() -> Option<PathBuf> {
option_env!("RUSTC_TEST_SUITE").map(PathBuf::from)
}
+#[must_use]
fn rustc_lib_path() -> PathBuf {
option_env!("RUSTC_LIB_PATH").unwrap().into()
}
#![allow(dead_code)]
-/// Used to test that certain lints don't trigger in imported external macros
+//! Used to test that certain lints don't trigger in imported external macros
+
#[macro_export]
macro_rules! foofoo {
() => {
loop {}
};
}
+
+#[macro_export]
+macro_rules! must_use_unit {
+ () => {
+ #[must_use]
+ fn foo() {}
+ };
+}
--- /dev/null
+#![warn(clippy::double_must_use)]
+
+#[must_use]
+pub fn must_use_result() -> Result<(), ()> {
+ unimplemented!();
+}
+
+#[must_use]
+pub fn must_use_tuple() -> (Result<(), ()>, u8) {
+ unimplemented!();
+}
+
+#[must_use]
+pub fn must_use_array() -> [Result<(), ()>; 1] {
+ unimplemented!();
+}
+
+#[must_use = "With note"]
+pub fn must_use_with_note() -> Result<(), ()> {
+ unimplemented!();
+}
+
+fn main() {
+ must_use_result();
+ must_use_tuple();
+ must_use_with_note();
+}
--- /dev/null
+error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+ --> $DIR/double_must_use.rs:4:1
+ |
+LL | pub fn must_use_result() -> Result<(), ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::double-must-use` implied by `-D warnings`
+ = help: either add some descriptive text or remove the attribute
+
+error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+ --> $DIR/double_must_use.rs:9:1
+ |
+LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: either add some descriptive text or remove the attribute
+
+error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+ --> $DIR/double_must_use.rs:14:1
+ |
+LL | pub fn must_use_array() -> [Result<(), ()>; 1] {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: either add some descriptive text or remove the attribute
+
+error: aborting due to 3 previous errors
+
--> $DIR/functions.rs:8:1
|
LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::too-many-arguments` implied by `-D warnings`
... |
LL | | eight: ()
LL | | ) {
- | |_^
+ | |__^
error: this function has too many arguments (8/7)
--> $DIR/functions.rs:45:5
|
LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this function has too many arguments (8/7)
--> $DIR/functions.rs:54:5
|
LL | fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this public function dereferences a raw pointer but is not marked `unsafe`
--> $DIR/functions.rs:63:34
#![warn(clippy::all, clippy::pedantic, clippy::option_unwrap_used)]
#![allow(
clippy::blacklisted_name,
- unused,
- clippy::print_stdout,
+ clippy::default_trait_access,
+ clippy::missing_docs_in_private_items,
clippy::non_ascii_literal,
clippy::new_without_default,
- clippy::missing_docs_in_private_items,
clippy::needless_pass_by_value,
- clippy::default_trait_access,
+ clippy::print_stdout,
+ clippy::must_use_candidate,
clippy::use_self,
clippy::useless_format,
- clippy::wrong_self_convention
+ clippy::wrong_self_convention,
+ unused
)]
#[macro_use]
error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name
- --> $DIR/methods.rs:36:5
+ --> $DIR/methods.rs:37:5
|
LL | / pub fn add(self, other: T) -> T {
LL | | self
= note: `-D clippy::should-implement-trait` implied by `-D warnings`
error: methods called `new` usually return `Self`
- --> $DIR/methods.rs:152:5
+ --> $DIR/methods.rs:153:5
|
LL | / fn new() -> i32 {
LL | | 0
= note: `-D clippy::new-ret-no-self` implied by `-D warnings`
error: called `map(f).unwrap_or(a)` on an Option value. This can be done more directly by calling `map_or(a, f)` instead
- --> $DIR/methods.rs:174:13
+ --> $DIR/methods.rs:175:13
|
LL | let _ = opt.map(|x| x + 1)
| _____________^
= note: replace `map(|x| x + 1).unwrap_or(0)` with `map_or(0, |x| x + 1)`
error: called `map(f).unwrap_or(a)` on an Option value. This can be done more directly by calling `map_or(a, f)` instead
- --> $DIR/methods.rs:178:13
+ --> $DIR/methods.rs:179:13
|
LL | let _ = opt.map(|x| {
| _____________^
| |____________________________^
error: called `map(f).unwrap_or(a)` on an Option value. This can be done more directly by calling `map_or(a, f)` instead
- --> $DIR/methods.rs:182:13
+ --> $DIR/methods.rs:183:13
|
LL | let _ = opt.map(|x| x + 1)
| _____________^
| |__________________^
error: called `map(f).unwrap_or(None)` on an Option value. This can be done more directly by calling `and_then(f)` instead
- --> $DIR/methods.rs:187:13
+ --> $DIR/methods.rs:188:13
|
LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: replace `map(|x| Some(x + 1)).unwrap_or(None)` with `and_then(|x| Some(x + 1))`
error: called `map(f).unwrap_or(None)` on an Option value. This can be done more directly by calling `and_then(f)` instead
- --> $DIR/methods.rs:189:13
+ --> $DIR/methods.rs:190:13
|
LL | let _ = opt.map(|x| {
| _____________^
| |_____________________^
error: called `map(f).unwrap_or(None)` on an Option value. This can be done more directly by calling `and_then(f)` instead
- --> $DIR/methods.rs:193:13
+ --> $DIR/methods.rs:194:13
|
LL | let _ = opt
| _____________^
= note: replace `map(|x| Some(x + 1)).unwrap_or(None)` with `and_then(|x| Some(x + 1))`
error: called `map(f).unwrap_or(a)` on an Option value. This can be done more directly by calling `map_or(a, f)` instead
- --> $DIR/methods.rs:204:13
+ --> $DIR/methods.rs:205:13
|
LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: replace `map(|p| format!("{}.", p)).unwrap_or(id)` with `map_or(id, |p| format!("{}.", p))`
error: called `map(f).unwrap_or_else(g)` on an Option value. This can be done more directly by calling `map_or_else(g, f)` instead
- --> $DIR/methods.rs:208:13
+ --> $DIR/methods.rs:209:13
|
LL | let _ = opt.map(|x| x + 1)
| _____________^
= note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)`
error: called `map(f).unwrap_or_else(g)` on an Option value. This can be done more directly by calling `map_or_else(g, f)` instead
- --> $DIR/methods.rs:212:13
+ --> $DIR/methods.rs:213:13
|
LL | let _ = opt.map(|x| {
| _____________^
| |____________________________________^
error: called `map(f).unwrap_or_else(g)` on an Option value. This can be done more directly by calling `map_or_else(g, f)` instead
- --> $DIR/methods.rs:216:13
+ --> $DIR/methods.rs:217:13
|
LL | let _ = opt.map(|x| x + 1)
| _____________^
| |_________________^
error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead.
- --> $DIR/methods.rs:246:13
+ --> $DIR/methods.rs:247:13
|
LL | let _ = v.iter().filter(|&x| *x < 0).next();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)`
error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead.
- --> $DIR/methods.rs:249:13
+ --> $DIR/methods.rs:250:13
|
LL | let _ = v.iter().filter(|&x| {
| _____________^
| |___________________________^
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:266:22
+ --> $DIR/methods.rs:267:22
|
LL | let _ = v.iter().find(|&x| *x < 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)`
= note: `-D clippy::search-is-some` implied by `-D warnings`
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:267:20
+ --> $DIR/methods.rs:268:20
|
LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)`
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:268:20
+ --> $DIR/methods.rs:269:20
|
LL | let _ = (0..1).find(|x| *x == 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)`
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:269:22
+ --> $DIR/methods.rs:270:22
|
LL | let _ = v.iter().find(|x| **x == 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)`
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:272:13
+ --> $DIR/methods.rs:273:13
|
LL | let _ = v.iter().find(|&x| {
| _____________^
| |______________________________^
error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:278:22
+ --> $DIR/methods.rs:279:22
|
LL | let _ = v.iter().position(|&x| x < 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)`
error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:281:13
+ --> $DIR/methods.rs:282:13
|
LL | let _ = v.iter().position(|&x| {
| _____________^
| |______________________________^
error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:287:22
+ --> $DIR/methods.rs:288:22
|
LL | let _ = v.iter().rposition(|&x| x < 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)`
error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`.
- --> $DIR/methods.rs:290:13
+ --> $DIR/methods.rs:291:13
|
LL | let _ = v.iter().rposition(|&x| {
| _____________^
| |______________________________^
error: used unwrap() on an Option value. If you don't want to handle the None case gracefully, consider using expect() to provide a better panic message
- --> $DIR/methods.rs:305:13
+ --> $DIR/methods.rs:306:13
|
LL | let _ = opt.unwrap();
| ^^^^^^^^^^^^
--- /dev/null
+// run-rustfix
+#![feature(never_type)]
+#![allow(unused_mut)]
+#![warn(clippy::must_use_candidate)]
+use std::rc::Rc;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
+
+pub struct MyAtomic(AtomicBool);
+pub struct MyPure;
+
+#[must_use] pub fn pure(i: u8) -> u8 {
+ i
+}
+
+impl MyPure {
+ #[must_use] pub fn inherent_pure(&self) -> u8 {
+ 0
+ }
+}
+
+pub trait MyPureTrait {
+ fn trait_pure(&self, i: u32) -> u32 {
+ self.trait_impl_pure(i) + 1
+ }
+
+ fn trait_impl_pure(&self, i: u32) -> u32;
+}
+
+impl MyPureTrait for MyPure {
+ #[must_use] fn trait_impl_pure(&self, i: u32) -> u32 {
+ i
+ }
+}
+
+pub fn without_result() {
+ // OK
+}
+
+pub fn impure_primitive(i: &mut u8) -> u8 {
+ *i
+}
+
+pub fn with_callback<F: Fn(u32) -> bool>(f: &F) -> bool {
+ f(0)
+}
+
+#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool {
+ true
+}
+
+pub fn quoth_the_raven(_more: !) -> u32 {
+ unimplemented!();
+}
+
+pub fn atomics(b: &AtomicBool) -> bool {
+ b.load(Ordering::SeqCst)
+}
+
+#[must_use] pub fn rcd(_x: Rc<u32>) -> bool {
+ true
+}
+
+pub fn rcmut(_x: Rc<&mut u32>) -> bool {
+ true
+}
+
+#[must_use] pub fn arcd(_x: Arc<u32>) -> bool {
+ false
+}
+
+pub fn inner_types(_m: &MyAtomic) -> bool {
+ true
+}
+
+static mut COUNTER: usize = 0;
+
+/// # Safety
+///
+/// Don't ever call this from multiple threads
+pub unsafe fn mutates_static() -> usize {
+ COUNTER += 1;
+ COUNTER
+}
+
+fn main() {
+ assert_eq!(1, pure(1));
+}
--- /dev/null
+// run-rustfix
+#![feature(never_type)]
+#![allow(unused_mut)]
+#![warn(clippy::must_use_candidate)]
+use std::rc::Rc;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
+
+pub struct MyAtomic(AtomicBool);
+pub struct MyPure;
+
+pub fn pure(i: u8) -> u8 {
+ i
+}
+
+impl MyPure {
+ pub fn inherent_pure(&self) -> u8 {
+ 0
+ }
+}
+
+pub trait MyPureTrait {
+ fn trait_pure(&self, i: u32) -> u32 {
+ self.trait_impl_pure(i) + 1
+ }
+
+ fn trait_impl_pure(&self, i: u32) -> u32;
+}
+
+impl MyPureTrait for MyPure {
+ fn trait_impl_pure(&self, i: u32) -> u32 {
+ i
+ }
+}
+
+pub fn without_result() {
+ // OK
+}
+
+pub fn impure_primitive(i: &mut u8) -> u8 {
+ *i
+}
+
+pub fn with_callback<F: Fn(u32) -> bool>(f: &F) -> bool {
+ f(0)
+}
+
+pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool {
+ true
+}
+
+pub fn quoth_the_raven(_more: !) -> u32 {
+ unimplemented!();
+}
+
+pub fn atomics(b: &AtomicBool) -> bool {
+ b.load(Ordering::SeqCst)
+}
+
+pub fn rcd(_x: Rc<u32>) -> bool {
+ true
+}
+
+pub fn rcmut(_x: Rc<&mut u32>) -> bool {
+ true
+}
+
+pub fn arcd(_x: Arc<u32>) -> bool {
+ false
+}
+
+pub fn inner_types(_m: &MyAtomic) -> bool {
+ true
+}
+
+static mut COUNTER: usize = 0;
+
+/// # Safety
+///
+/// Don't ever call this from multiple threads
+pub unsafe fn mutates_static() -> usize {
+ COUNTER += 1;
+ COUNTER
+}
+
+fn main() {
+ assert_eq!(1, pure(1));
+}
--- /dev/null
+error: this function could have a `#[must_use]` attribute
+ --> $DIR/must_use_candidates.rs:12:1
+ |
+LL | pub fn pure(i: u8) -> u8 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn pure(i: u8) -> u8`
+ |
+ = note: `-D clippy::must-use-candidate` implied by `-D warnings`
+
+error: this method could have a `#[must_use]` attribute
+ --> $DIR/must_use_candidates.rs:17:5
+ |
+LL | pub fn inherent_pure(&self) -> u8 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn inherent_pure(&self) -> u8`
+
+error: this method could have a `#[must_use]` attribute
+ --> $DIR/must_use_candidates.rs:31:5
+ |
+LL | fn trait_impl_pure(&self, i: u32) -> u32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] fn trait_impl_pure(&self, i: u32) -> u32`
+
+error: this function could have a `#[must_use]` attribute
+ --> $DIR/must_use_candidates.rs:48:1
+ |
+LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool`
+
+error: this function could have a `#[must_use]` attribute
+ --> $DIR/must_use_candidates.rs:60:1
+ |
+LL | pub fn rcd(_x: Rc<u32>) -> bool {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn rcd(_x: Rc<u32>) -> bool`
+
+error: this function could have a `#[must_use]` attribute
+ --> $DIR/must_use_candidates.rs:68:1
+ |
+LL | pub fn arcd(_x: Arc<u32>) -> bool {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn arcd(_x: Arc<u32>) -> bool`
+
+error: aborting due to 6 previous errors
+
--- /dev/null
+//run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::must_use_unit)]
+#![allow(clippy::unused_unit)]
+
+#[macro_use]
+extern crate macro_rules;
+
+
+pub fn must_use_default() {}
+
+
+pub fn must_use_unit() -> () {}
+
+
+pub fn must_use_with_note() {}
+
+fn main() {
+ must_use_default();
+ must_use_unit();
+ must_use_with_note();
+
+ // We should not lint in external macros
+ must_use_unit!();
+}
--- /dev/null
+//run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::must_use_unit)]
+#![allow(clippy::unused_unit)]
+
+#[macro_use]
+extern crate macro_rules;
+
+#[must_use]
+pub fn must_use_default() {}
+
+#[must_use]
+pub fn must_use_unit() -> () {}
+
+#[must_use = "With note"]
+pub fn must_use_with_note() {}
+
+fn main() {
+ must_use_default();
+ must_use_unit();
+ must_use_with_note();
+
+ // We should not lint in external macros
+ must_use_unit!();
+}
--- /dev/null
+error: this unit-returning function has a `#[must_use]` attribute
+ --> $DIR/must_use_unit.rs:11:1
+ |
+LL | #[must_use]
+ | ----------- help: remove the attribute
+LL | pub fn must_use_default() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::must-use-unit` implied by `-D warnings`
+
+error: this unit-returning function has a `#[must_use]` attribute
+ --> $DIR/must_use_unit.rs:14:1
+ |
+LL | #[must_use]
+ | ----------- help: remove the attribute
+LL | pub fn must_use_unit() -> () {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this unit-returning function has a `#[must_use]` attribute
+ --> $DIR/must_use_unit.rs:17:1
+ |
+LL | #[must_use = "With note"]
+ | ------------------------- help: remove the attribute
+LL | pub fn must_use_with_note() {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
x
}
+#[must_use]
fn first(x: (isize, isize)) -> isize {
x.0
}
error: `x` is shadowed by itself in `&mut x`
- --> $DIR/shadow.rs:25:5
+ --> $DIR/shadow.rs:26:5
|
LL | let x = &mut x;
| ^^^^^^^^^^^^^^^
|
= note: `-D clippy::shadow-same` implied by `-D warnings`
note: previous binding is here
- --> $DIR/shadow.rs:24:13
+ --> $DIR/shadow.rs:25:13
|
LL | let mut x = 1;
| ^
error: `x` is shadowed by itself in `{ x }`
- --> $DIR/shadow.rs:26:5
+ --> $DIR/shadow.rs:27:5
|
LL | let x = { x };
| ^^^^^^^^^^^^^^
|
note: previous binding is here
- --> $DIR/shadow.rs:25:9
+ --> $DIR/shadow.rs:26:9
|
LL | let x = &mut x;
| ^
error: `x` is shadowed by itself in `(&*x)`
- --> $DIR/shadow.rs:27:5
+ --> $DIR/shadow.rs:28:5
|
LL | let x = (&*x);
| ^^^^^^^^^^^^^^
|
note: previous binding is here
- --> $DIR/shadow.rs:26:9
+ --> $DIR/shadow.rs:27:9
|
LL | let x = { x };
| ^
error: `x` is shadowed by `{ *x + 1 }` which reuses the original value
- --> $DIR/shadow.rs:28:9
+ --> $DIR/shadow.rs:29:9
|
LL | let x = { *x + 1 };
| ^
|
= note: `-D clippy::shadow-reuse` implied by `-D warnings`
note: initialization happens here
- --> $DIR/shadow.rs:28:13
+ --> $DIR/shadow.rs:29:13
|
LL | let x = { *x + 1 };
| ^^^^^^^^^^
note: previous binding is here
- --> $DIR/shadow.rs:27:9
+ --> $DIR/shadow.rs:28:9
|
LL | let x = (&*x);
| ^
error: `x` is shadowed by `id(x)` which reuses the original value
- --> $DIR/shadow.rs:29:9
+ --> $DIR/shadow.rs:30:9
|
LL | let x = id(x);
| ^
|
note: initialization happens here
- --> $DIR/shadow.rs:29:13
+ --> $DIR/shadow.rs:30:13
|
LL | let x = id(x);
| ^^^^^
note: previous binding is here
- --> $DIR/shadow.rs:28:9
+ --> $DIR/shadow.rs:29:9
|
LL | let x = { *x + 1 };
| ^
error: `x` is shadowed by `(1, x)` which reuses the original value
- --> $DIR/shadow.rs:30:9
+ --> $DIR/shadow.rs:31:9
|
LL | let x = (1, x);
| ^
|
note: initialization happens here
- --> $DIR/shadow.rs:30:13
+ --> $DIR/shadow.rs:31:13
|
LL | let x = (1, x);
| ^^^^^^
note: previous binding is here
- --> $DIR/shadow.rs:29:9
+ --> $DIR/shadow.rs:30:9
|
LL | let x = id(x);
| ^
error: `x` is shadowed by `first(x)` which reuses the original value
- --> $DIR/shadow.rs:31:9
+ --> $DIR/shadow.rs:32:9
|
LL | let x = first(x);
| ^
|
note: initialization happens here
- --> $DIR/shadow.rs:31:13
+ --> $DIR/shadow.rs:32:13
|
LL | let x = first(x);
| ^^^^^^^^
note: previous binding is here
- --> $DIR/shadow.rs:30:9
+ --> $DIR/shadow.rs:31:9
|
LL | let x = (1, x);
| ^
error: `x` is shadowed by `y`
- --> $DIR/shadow.rs:33:9
+ --> $DIR/shadow.rs:34:9
|
LL | let x = y;
| ^
|
= note: `-D clippy::shadow-unrelated` implied by `-D warnings`
note: initialization happens here
- --> $DIR/shadow.rs:33:13
+ --> $DIR/shadow.rs:34:13
|
LL | let x = y;
| ^
note: previous binding is here
- --> $DIR/shadow.rs:31:9
+ --> $DIR/shadow.rs:32:9
|
LL | let x = first(x);
| ^
error: `x` shadows a previous declaration
- --> $DIR/shadow.rs:35:5
+ --> $DIR/shadow.rs:36:5
|
LL | let x;
| ^^^^^^
|
note: previous binding is here
- --> $DIR/shadow.rs:33:9
+ --> $DIR/shadow.rs:34:9
|
LL | let x = y;
| ^