diff options
| author | bors <bors@rust-lang.org> | 2019-10-14 17:33:30 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2019-10-14 17:33:30 +0000 |
| commit | 8fae2dd3c1bfed13bdd6c0cfd4170dd1363f25f7 (patch) | |
| tree | 5191a1ef86462ec213300758e44a6ee6f25fdd55 /clippy_lints/src | |
| parent | c40d7db6ed04ce0a2f8ce5b0e057615850d69f48 (diff) | |
| parent | cc622608db7318b1c0fe3ccd541558436c7c6c4c (diff) | |
| download | rust-8fae2dd3c1bfed13bdd6c0cfd4170dd1363f25f7.tar.gz rust-8fae2dd3c1bfed13bdd6c0cfd4170dd1363f25f7.zip | |
Auto merge of #4560 - rust-lang:must-use-pure, r=phansch
new lints around`#[must_use]` changelog: Add `must_use_candidate` lint, add `must-use-unit` lint, add `double_must_use` lint The first one checks if an public function or method has no mutable argument and mutates no non-local data and lints if it has no `#[must_use]` attribute. It will skip inner functions, because those are usually highly local and the attribute doesn't have as much benefit there. The second lints `#[must_use]` attributes on functions and methods that return unit. Those attributes are likely a remnant from a refactoring that removed the return value. The last one lints for `#[must_use]` attributrs without text on functions that return a type which is already marked `#[must_use]`. This has no auto-suggestion, because while it would be easy to do, there may be value in writing a detailed text for the attribute instead. This fixes #4526
Diffstat (limited to 'clippy_lints/src')
38 files changed, 465 insertions, 29 deletions
diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 7578b5fffe1..04530542ef8 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -93,6 +93,7 @@ fn check_known_consts(cx: &LateContext<'_, '_>, e: &Expr, s: symbol::Symbol, mod /// 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 diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index a16c4575e70..3d12bb347aa 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -229,6 +229,7 @@ fn lint_misrefactored_assign_op( ); } +#[must_use] fn is_commutative(op: hir::BinOpKind) -> bool { use rustc::hir::BinOpKind::*; match op { diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs index e27b5269ef4..6d68c319f4c 100644 --- a/clippy_lints/src/bit_mask.rs +++ b/clippy_lints/src/bit_mask.rs @@ -100,6 +100,7 @@ pub struct BitMask { } impl BitMask { + #[must_use] pub fn new(verbose_bit_mask_threshold: u64) -> Self { Self { verbose_bit_mask_threshold, @@ -150,6 +151,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BitMask { } } +#[must_use] fn invert_cmp(cmp: BinOpKind) -> BinOpKind { match cmp { BinOpKind::Eq => BinOpKind::Eq, diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index d1d725678b7..dfd497f909b 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -160,6 +160,7 @@ impl<'a> Conversion<'a> { 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) diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index d869427467c..370421190cb 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -29,6 +29,7 @@ pub struct CognitiveComplexity { } impl CognitiveComplexity { + #[must_use] pub fn new(limit: u64) -> Self { Self { limit: LimitStack::new(limit), diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 0f95efe59f1..726a044f9ed 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -191,6 +191,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DocMarkdown { /// 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] = &["///!", "///", "//!", "//"]; diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index e64eed38340..b64e97fba5c 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -107,6 +107,7 @@ pub struct EnumVariantNames { } impl EnumVariantNames { + #[must_use] pub fn new(threshold: u64) -> Self { Self { modules: Vec::new(), @@ -123,6 +124,7 @@ impl_lint_pass!(EnumVariantNames => [ ]); /// 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 @@ -130,6 +132,7 @@ fn partial_match(pre: &str, name: &str) -> usize { } /// 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 @@ -211,6 +214,7 @@ fn check_variant( ); } +#[must_use] fn to_camel_case(item_name: &str) -> String { let mut s = String::new(); let mut up = true; diff --git a/clippy_lints/src/excessive_precision.rs b/clippy_lints/src/excessive_precision.rs index ae9681134ff..763770c74ef 100644 --- a/clippy_lints/src/excessive_precision.rs +++ b/clippy_lints/src/excessive_precision.rs @@ -62,6 +62,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExcessivePrecision { 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(); @@ -97,6 +98,7 @@ impl ExcessivePrecision { /// 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'); @@ -109,6 +111,7 @@ fn dot_zero_exclusion(s: &str) -> bool { }) } +#[must_use] fn max_digits(fty: FloatTy) -> u32 { match fty { FloatTy::F32 => f32::DIGITS, @@ -117,6 +120,7 @@ fn max_digits(fty: FloatTy) -> u32 { } /// 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() @@ -138,6 +142,7 @@ enum FloatFormat { Normal, } impl FloatFormat { + #[must_use] fn new(s: &str) -> Self { s.chars() .find_map(|x| match x { diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 821e79e8c77..62606257498 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -235,6 +235,7 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { } } +#[must_use] fn has_unary_equivalent(bin_op: BinOpKind) -> bool { // &, *, - bin_op == BinOpKind::And || bin_op == BinOpKind::Mul || bin_op == BinOpKind::Sub diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 7b6c8c7cea6..6052f936109 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -1,16 +1,17 @@ -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. @@ -84,6 +85,80 @@ declare_clippy_lint! { "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, @@ -96,7 +171,14 @@ impl Functions { } } -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( @@ -134,7 +216,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions { _, ) | 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())) }, _ => {}, } @@ -144,42 +226,88 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions { 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), ); } @@ -268,6 +396,164 @@ impl<'a, 'tcx> Functions { } } +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) @@ -282,7 +568,7 @@ struct DerefVisitor<'a, 'tcx> { 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) => { @@ -308,8 +594,9 @@ impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { _ => (), } - 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 } @@ -331,3 +618,72 @@ impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { } } } + +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 +} diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index adda6ae0c11..e6af8e8c7fc 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -67,6 +67,7 @@ enum Finiteness { use self::Finiteness::{Finite, Infinite, MaybeInfinite}; impl Finiteness { + #[must_use] fn and(self, b: Self) -> Self { match (self, b) { (Finite, _) | (_, Finite) => Finite, @@ -75,6 +76,7 @@ impl Finiteness { } } + #[must_use] fn or(self, b: Self) -> Self { match (self, b) { (Infinite, _) | (_, Infinite) => Infinite, @@ -85,6 +87,7 @@ impl Finiteness { } impl From<bool> for Finiteness { + #[must_use] fn from(b: bool) -> Self { if b { Infinite diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index d5c1318f677..d612a4326fe 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -35,6 +35,7 @@ pub struct LargeEnumVariant { } impl LargeEnumVariant { + #[must_use] pub fn new(maximum_size_difference_allowed: u64) -> Self { Self { maximum_size_difference_allowed, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ec12e06b1aa..5d428221e69 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -6,7 +6,7 @@ #![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)] @@ -648,6 +648,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con 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, @@ -744,6 +745,8 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con 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, @@ -955,6 +958,8 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con 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, diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 88d393c84ee..be1e65fc17c 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -270,6 +270,7 @@ fn lts_from_bounds<'a, T: Iterator<Item = &'a Lifetime>>(mut vec: Vec<RefLt>, bo } /// Number of unique lifetimes in the given vector. +#[must_use] fn unique_lifetimes(lts: &[RefLt]) -> usize { lts.iter().collect::<FxHashSet<_>>().len() } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 5cdbfe1099c..4b2bb69fa79 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -113,6 +113,7 @@ pub(super) enum Radix { 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, @@ -136,6 +137,7 @@ pub(super) struct DigitInfo<'a> { } 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") { @@ -422,6 +424,7 @@ impl LiteralDigitGrouping { /// 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. @@ -499,6 +502,7 @@ impl EarlyLintPass for DecimalLiteralRepresentation { } impl DecimalLiteralRepresentation { + #[must_use] pub fn new(threshold: u64) -> Self { Self { threshold } } @@ -573,22 +577,27 @@ impl DecimalLiteralRepresentation { } } +#[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") } diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index ea496a0294a..22821e02fe2 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -610,6 +610,7 @@ enum NeverLoopResult { Otherwise, } +#[must_use] fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult { match *arg { NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise, @@ -618,6 +619,7 @@ fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult { } // 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, @@ -626,6 +628,7 @@ fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResu } // 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) => { @@ -637,6 +640,7 @@ fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResul } // 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, diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 70f324a5081..8b2bcce471c 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -195,6 +195,7 @@ fn let_binding_name(cx: &LateContext<'_, '_>, var_arg: &hir::Expr) -> String { } } +#[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}", diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 198f97e1f56..848e3bcb881 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2898,6 +2898,7 @@ impl SelfKind { } } + #[must_use] fn description(self) -> &'static str { match self { Self::Value => "self by value", @@ -2909,6 +2910,7 @@ impl SelfKind { } impl Convention { + #[must_use] fn check(&self, other: &str) -> bool { match *self { Self::Eq(this) => this == other, diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 43a5e5c1b4b..c06eec95028 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -253,6 +253,7 @@ struct ReturnVisitor { } impl ReturnVisitor { + #[must_use] fn new() -> Self { Self { found_return: false } } diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 2f20aa9c683..d97e3ed8806 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -128,6 +128,7 @@ fn method_accepts_dropable(cx: &LateContext<'_, '_>, param_tys: &HirVec<hir::Ty> } // 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 } diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index f09a864240b..949bd4bce27 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -37,12 +37,14 @@ pub struct MissingDoc { } 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], diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index bd9d1583493..8fa30f6827c 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -405,6 +405,7 @@ fn check_and_warn<'a>(ctx: &EarlyContext<'_>, expr: &'a ast::Expr) { /// /// 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 != '}') {} @@ -435,6 +436,7 @@ pub fn erode_from_back(s: &str) -> String { /// inside_a_block(); /// } /// ``` +#[must_use] pub fn erode_from_front(s: &str) -> String { s.chars() .skip_while(|c| c.is_whitespace()) @@ -447,6 +449,7 @@ pub fn erode_from_front(s: &str) -> String { /// 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)) } diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 33f1dde9872..9cddd812b52 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -92,6 +92,7 @@ enum Source { } impl Source { + #[must_use] fn lint(&self) -> (&'static Lint, &'static str, Span) { match self { Self::Item { item } | Self::Assoc { item, .. } => ( diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 25e8efe3cad..08a78b784ba 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -148,6 +148,7 @@ impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> { } } +#[must_use] fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> { for &allow in WHITELIST { if whitelisted(interned_name, allow) { @@ -157,6 +158,7 @@ fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> { 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)) @@ -383,6 +385,7 @@ fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attri } /// 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(); diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index b00062cd55a..a2d054c1de4 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -121,6 +121,7 @@ fn is_arith_expr(expr: &Expr) -> bool { } } +#[must_use] fn is_bit_op(op: BinOpKind) -> bool { use syntax::ast::BinOpKind::*; match op { @@ -129,6 +130,7 @@ fn is_bit_op(op: BinOpKind) -> bool { } } +#[must_use] fn is_arith_op(op: BinOpKind) -> bool { use syntax::ast::BinOpKind::*; match op { diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index d2e48e1d798..5c509158128 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -131,6 +131,7 @@ enum Method { } impl Method { + #[must_use] fn suggestion(self) -> &'static str { match self { Self::Offset => "add", diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 22c4b107f00..54220f1107b 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -129,6 +129,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Regex { } #[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); diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 47fdc226e99..a5323501207 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -318,6 +318,7 @@ fn attr_is_cfg(attr: &ast::Attribute) -> bool { } // 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) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 7230c0c08f2..be8165d51f5 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1372,6 +1372,7 @@ pub struct TypeComplexity { } impl TypeComplexity { + #[must_use] pub fn new(threshold: u64) -> Self { Self { threshold } } @@ -1780,6 +1781,7 @@ enum FullInt { impl FullInt { #[allow(clippy::cast_sign_loss)] + #[must_use] fn cmp_s_u(s: i128, u: u128) -> Ordering { if s < 0 { Ordering::Less @@ -1792,12 +1794,14 @@ impl FullInt { } 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), @@ -1808,6 +1812,7 @@ impl PartialOrd for FullInt { } } 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") diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 29c8015edcf..528a1915be8 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -72,6 +72,7 @@ fn unsafe_to_safe_check(old_name: Ident, new_name: Ident, cx: &EarlyContext<'_>, } } +#[must_use] fn contains_unsafe(name: &LocalInternedString) -> bool { name.contains("Unsafe") || name.contains("unsafe") } diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index bec8d53714e..11340f69aa2 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -34,6 +34,7 @@ impl Drop for LimitStack { } impl LimitStack { + #[must_use] pub fn new(limit: u64) -> Self { Self { stack: vec![limit] } } diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index f36570904c0..a424c09ef40 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -146,6 +146,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Author { } impl PrintVisitor { + #[must_use] fn new(s: &'static str) -> Self { Self { ids: FxHashMap::default(), @@ -683,6 +684,7 @@ fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool { 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(), @@ -702,6 +704,7 @@ fn desugaring_name(des: hir::MatchSource) -> String { } } +#[must_use] fn loop_desugaring_name(des: hir::LoopSource) -> &'static str { match des { hir::LoopSource::ForLoop => "LoopSource::ForLoop", diff --git a/clippy_lints/src/utils/camel_case.rs b/clippy_lints/src/utils/camel_case.rs index 5b124dd96bf..4192a26d3c8 100644 --- a/clippy_lints/src/utils/camel_case.rs +++ b/clippy_lints/src/utils/camel_case.rs @@ -1,4 +1,5 @@ /// 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() { @@ -32,6 +33,7 @@ pub fn until(s: &str) -> usize { } /// 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() { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 4595819f557..734b689ab1a 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -90,6 +90,7 @@ macro_rules! define_Conf { } } + #[must_use] fn $rust_name() -> define_Conf!(TY $($ty)+) { define_Conf!(DEFAULT $($ty)+, $default) } @@ -153,6 +154,7 @@ define_Conf! { } impl Default for Conf { + #[must_use] fn default() -> Self { toml::from_str("").expect("we never error on empty config files") } diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs index de974eb3d27..63e9a27c545 100644 --- a/clippy_lints/src/utils/higher.rs +++ b/clippy_lints/src/utils/higher.rs @@ -10,6 +10,7 @@ use rustc::{hir, ty}; 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, diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 1ec89cb93f1..96b00eb154a 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -239,6 +239,7 @@ pub struct CompilerLintFunctions { } impl CompilerLintFunctions { + #[must_use] pub fn new() -> Self { let mut map = FxHashMap::default(); map.insert("span_lint", "utils::span_lint"); diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 88553bf1d76..a5e2f3dc4b4 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -52,6 +52,7 @@ use crate::reexport::*; /// 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() } @@ -98,6 +99,7 @@ pub fn in_constant(cx: &LateContext<'_, '_>, id: HirId) -> bool { } /// 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 { @@ -721,6 +723,7 @@ pub fn is_adjusted(cx: &LateContext<'_, '_>, e: &Expr) -> bool { /// 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() { @@ -748,6 +751,7 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> { /// `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(); diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 3c31067f7f8..0675c603341 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -417,6 +417,7 @@ enum Associativity { /// 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::*; |
