diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs
index 036820c6d7fa1..d540b3f7e40a3 100644
--- a/src/librustc_lint/unused.rs
+++ b/src/librustc_lint/unused.rs
@@ -1,7 +1,7 @@
 use rustc::hir::def::{Res, DefKind};
 use rustc::hir::def_id::DefId;
 use rustc::lint;
-use rustc::ty;
+use rustc::ty::{self, Ty};
 use rustc::ty::adjustment;
 use rustc_data_structures::fx::FxHashMap;
 use lint::{LateContext, EarlyContext, LintContext, LintArray};
@@ -47,43 +47,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
             return;
         }
 
-        let t = cx.tables.expr_ty(&expr);
-        let type_permits_lack_of_use = if t.is_unit()
-            || cx.tcx.is_ty_uninhabited_from(
-                cx.tcx.hir().get_module_parent_by_hir_id(expr.hir_id), t)
-        {
-            true
-        } else {
-            match t.sty {
-                ty::Adt(def, _) => check_must_use(cx, def.did, s.span, "", ""),
-                ty::Opaque(def, _) => {
-                    let mut must_use = false;
-                    for (predicate, _) in &cx.tcx.predicates_of(def).predicates {
-                        if let ty::Predicate::Trait(ref poly_trait_predicate) = predicate {
-                            let trait_ref = poly_trait_predicate.skip_binder().trait_ref;
-                            if check_must_use(cx, trait_ref.def_id, s.span, "implementer of ", "") {
-                                must_use = true;
-                                break;
-                            }
-                        }
-                    }
-                    must_use
-                }
-                ty::Dynamic(binder, _) => {
-                    let mut must_use = false;
-                    for predicate in binder.skip_binder().iter() {
-                        if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate {
-                            if check_must_use(cx, trait_ref.def_id, s.span, "", " trait object") {
-                                must_use = true;
-                                break;
-                            }
-                        }
-                    }
-                    must_use
-                }
-                _ => false,
-            }
-        };
+        let ty = cx.tables.expr_ty(&expr);
+        let type_permits_lack_of_use = check_must_use_ty(cx, ty, &expr, s.span, "");
 
         let mut fn_warned = false;
         let mut op_warned = false;
@@ -108,7 +73,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
             _ => None
         };
         if let Some(def_id) = maybe_def_id {
-            fn_warned = check_must_use(cx, def_id, s.span, "return value of ", "");
+            fn_warned = check_must_use_def(cx, def_id, s.span, "return value of ", "");
         } else if type_permits_lack_of_use {
             // We don't warn about unused unit or uninhabited types.
             // (See https://github.com/rust-lang/rust/issues/43806 for details.)
@@ -162,10 +127,75 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
             cx.span_lint(UNUSED_RESULTS, s.span, "unused result");
         }
 
-        fn check_must_use(
+        // Returns whether an error has been emitted (and thus another does not need to be later).
+        fn check_must_use_ty<'tcx>(
+            cx: &LateContext<'_, 'tcx>,
+            ty: Ty<'tcx>,
+            expr: &hir::Expr,
+            span: Span,
+            descr_post_path: &str,
+        ) -> bool {
+            if ty.is_unit() || cx.tcx.is_ty_uninhabited_from(
+                cx.tcx.hir().get_module_parent_by_hir_id(expr.hir_id), ty)
+            {
+                return true;
+            }
+
+            match ty.sty {
+                ty::Adt(def, _) => check_must_use_def(cx, def.did, span, "", descr_post_path),
+                ty::Opaque(def, _) => {
+                    let mut has_emitted = false;
+                    for (predicate, _) in &cx.tcx.predicates_of(def).predicates {
+                        if let ty::Predicate::Trait(ref poly_trait_predicate) = predicate {
+                            let trait_ref = poly_trait_predicate.skip_binder().trait_ref;
+                            let def_id = trait_ref.def_id;
+                            if check_must_use_def(cx, def_id, span, "implementer of ", "") {
+                                has_emitted = true;
+                                break;
+                            }
+                        }
+                    }
+                    has_emitted
+                }
+                ty::Dynamic(binder, _) => {
+                    let mut has_emitted = false;
+                    for predicate in binder.skip_binder().iter() {
+                        if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate {
+                            let def_id = trait_ref.def_id;
+                            if check_must_use_def(cx, def_id, span, "", " trait object") {
+                                has_emitted = true;
+                                break;
+                            }
+                        }
+                    }
+                    has_emitted
+                }
+                ty::Tuple(ref tys) => {
+                    let mut has_emitted = false;
+                    let spans = if let hir::ExprKind::Tup(comps) = &expr.node {
+                        debug_assert_eq!(comps.len(), tys.len());
+                        comps.iter().map(|e| e.span).collect()
+                    } else {
+                        vec![]
+                    };
+                    for (i, ty) in tys.iter().map(|k| k.expect_ty()).enumerate() {
+                        let descr_post_path = &format!(" in tuple element {}", i);
+                        let span = *spans.get(i).unwrap_or(&span);
+                        if check_must_use_ty(cx, ty, expr, span, descr_post_path) {
+                            has_emitted = true;
+                        }
+                    }
+                    has_emitted
+                }
+                _ => false,
+            }
+        }
+
+        // Returns whether an error has been emitted (and thus another does not need to be later).
+        fn check_must_use_def(
             cx: &LateContext<'_, '_>,
             def_id: DefId,
-            sp: Span,
+            span: Span,
             descr_pre_path: &str,
             descr_post_path: &str,
         ) -> bool {
@@ -173,7 +203,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
                 if attr.check_name(sym::must_use) {
                     let msg = format!("unused {}`{}`{} that must be used",
                         descr_pre_path, cx.tcx.def_path_str(def_id), descr_post_path);
-                    let mut err = cx.struct_span_lint(UNUSED_MUST_USE, sp, &msg);
+                    let mut err = cx.struct_span_lint(UNUSED_MUST_USE, span, &msg);
                     // check for #[must_use = "..."]
                     if let Some(note) = attr.value_str() {
                         err.note(&note.as_str());
diff --git a/src/test/ui/lint/must_use-tuple.rs b/src/test/ui/lint/must_use-tuple.rs
new file mode 100644
index 0000000000000..f6b579a7f35cf
--- /dev/null
+++ b/src/test/ui/lint/must_use-tuple.rs
@@ -0,0 +1,17 @@
+#![deny(unused_must_use)]
+
+fn foo() -> (Result<(), ()>, ()) {
+    (Ok::<(), ()>(()), ())
+}
+
+fn main() {
+    (Ok::<(), ()>(()),); //~ ERROR unused `std::result::Result`
+
+    (Ok::<(), ()>(()), 0, Ok::<(), ()>(()), 5);
+    //~^ ERROR unused `std::result::Result`
+    //~^^ ERROR unused `std::result::Result`
+
+    foo(); //~ ERROR unused `std::result::Result`
+
+    ((Err::<(), ()>(()), ()), ()); //~ ERROR unused `std::result::Result`
+}
diff --git a/src/test/ui/lint/must_use-tuple.stderr b/src/test/ui/lint/must_use-tuple.stderr
new file mode 100644
index 0000000000000..45d2a439e52b0
--- /dev/null
+++ b/src/test/ui/lint/must_use-tuple.stderr
@@ -0,0 +1,47 @@
+error: unused `std::result::Result` in tuple element 0 that must be used
+  --> $DIR/must_use-tuple.rs:8:6
+   |
+LL |     (Ok::<(), ()>(()),);
+   |      ^^^^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/must_use-tuple.rs:1:9
+   |
+LL | #![deny(unused_must_use)]
+   |         ^^^^^^^^^^^^^^^
+   = note: this `Result` may be an `Err` variant, which should be handled
+
+error: unused `std::result::Result` in tuple element 0 that must be used
+  --> $DIR/must_use-tuple.rs:10:6
+   |
+LL |     (Ok::<(), ()>(()), 0, Ok::<(), ()>(()), 5);
+   |      ^^^^^^^^^^^^^^^^
+   |
+   = note: this `Result` may be an `Err` variant, which should be handled
+
+error: unused `std::result::Result` in tuple element 2 that must be used
+  --> $DIR/must_use-tuple.rs:10:27
+   |
+LL |     (Ok::<(), ()>(()), 0, Ok::<(), ()>(()), 5);
+   |                           ^^^^^^^^^^^^^^^^
+   |
+   = note: this `Result` may be an `Err` variant, which should be handled
+
+error: unused `std::result::Result` in tuple element 0 that must be used
+  --> $DIR/must_use-tuple.rs:14:5
+   |
+LL |     foo();
+   |     ^^^^^^
+   |
+   = note: this `Result` may be an `Err` variant, which should be handled
+
+error: unused `std::result::Result` in tuple element 0 that must be used
+  --> $DIR/must_use-tuple.rs:16:6
+   |
+LL |     ((Err::<(), ()>(()), ()), ());
+   |      ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this `Result` may be an `Err` variant, which should be handled
+
+error: aborting due to 5 previous errors
+