summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/mut_mut.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/clippy/clippy_lints/src/mut_mut.rs')
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_mut.rs114
1 files changed, 114 insertions, 0 deletions
diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs
new file mode 100644
index 000000000..cb16f0004
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs
@@ -0,0 +1,114 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::higher;
+use rustc_hir as hir;
+use rustc_hir::intravisit;
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for instances of `mut mut` references.
+ ///
+ /// ### Why is this bad?
+ /// Multiple `mut`s don't add anything meaningful to the
+ /// source. This is either a copy'n'paste error, or it shows a fundamental
+ /// misunderstanding of references.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let mut y = 1;
+ /// let x = &mut &mut y;
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub MUT_MUT,
+ pedantic,
+ "usage of double-mut refs, e.g., `&mut &mut ...`"
+}
+
+declare_lint_pass!(MutMut => [MUT_MUT]);
+
+impl<'tcx> LateLintPass<'tcx> for MutMut {
+ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
+ intravisit::walk_block(&mut MutVisitor { cx }, block);
+ }
+
+ fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_>) {
+ use rustc_hir::intravisit::Visitor;
+
+ MutVisitor { cx }.visit_ty(ty);
+ }
+}
+
+pub struct MutVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+}
+
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
+ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
+ if in_external_macro(self.cx.sess(), expr.span) {
+ return;
+ }
+
+ if let Some(higher::ForLoop { arg, body, .. }) = higher::ForLoop::hir(expr) {
+ // A `for` loop lowers to:
+ // ```rust
+ // match ::std::iter::Iterator::next(&mut iter) {
+ // // ^^^^
+ // ```
+ // Let's ignore the generated code.
+ intravisit::walk_expr(self, arg);
+ intravisit::walk_expr(self, body);
+ } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind {
+ if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind {
+ span_lint(
+ self.cx,
+ MUT_MUT,
+ expr.span,
+ "generally you want to avoid `&mut &mut _` if possible",
+ );
+ } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() {
+ span_lint(
+ self.cx,
+ MUT_MUT,
+ expr.span,
+ "this expression mutably borrows a mutable reference. Consider reborrowing",
+ );
+ }
+ }
+ }
+
+ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
+ if in_external_macro(self.cx.sess(), ty.span) {
+ return;
+ }
+
+ if let hir::TyKind::Rptr(
+ _,
+ hir::MutTy {
+ ty: pty,
+ mutbl: hir::Mutability::Mut,
+ },
+ ) = ty.kind
+ {
+ if let hir::TyKind::Rptr(
+ _,
+ hir::MutTy {
+ mutbl: hir::Mutability::Mut,
+ ..
+ },
+ ) = pty.kind
+ {
+ span_lint(
+ self.cx,
+ MUT_MUT,
+ ty.span,
+ "generally you want to avoid `&mut &mut _` if possible",
+ );
+ }
+ }
+
+ intravisit::walk_ty(self, ty);
+ }
+}