diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index acc433f2bc86be2d39971a92af44a0f320b11106..86d0a3e7303f14f76e0cc3037fd43d22f44f4023 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -110,6 +110,9 @@ Improvements to Clang's diagnostics
   <https://github.com/llvm/llvm-project/issues/50794>`_.
 - ``-Wunused-but-set-variable`` now also warns if the variable is only used
   by unary operators.
+- ``-Wunused-variable`` no longer warn for references extending the lifetime
+  of temporaries with side effects. This fixes `Issue 54489
+  <https://github.com/llvm/llvm-project/issues/54489>`_.
 
 Non-comprehensive list of changes in this release
 -------------------------------------------------
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 0b5b530bc756c8c2efabc44d6fe27a41b2d0b5ba..a2d3722f2efb841e2275babbd99785acc3a8463a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -1889,15 +1889,28 @@ static bool ShouldDiagnoseUnusedDecl(const NamedDecl *D) {
   // Types of valid local variables should be complete, so this should succeed.
   if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
 
-    // White-list anything with an __attribute__((unused)) type.
+    const Expr *Init = VD->getInit();
+    if (const auto *Cleanups = dyn_cast_or_null<ExprWithCleanups>(Init))
+      Init = Cleanups->getSubExpr();
+
     const auto *Ty = VD->getType().getTypePtr();
 
     // Only look at the outermost level of typedef.
     if (const TypedefType *TT = Ty->getAs<TypedefType>()) {
+      // Allow anything marked with __attribute__((unused)).
       if (TT->getDecl()->hasAttr<UnusedAttr>())
         return false;
     }
 
+    // Warn for reference variables whose initializtion performs lifetime
+    // extension.
+    if (const auto *MTE = dyn_cast_or_null<MaterializeTemporaryExpr>(Init)) {
+      if (MTE->getExtendingDecl()) {
+        Ty = VD->getType().getNonReferenceType().getTypePtr();
+        Init = MTE->getSubExpr()->IgnoreImplicitAsWritten();
+      }
+    }
+
     // If we failed to complete the type for some reason, or if the type is
     // dependent, don't diagnose the variable.
     if (Ty->isIncompleteType() || Ty->isDependentType())
@@ -1916,10 +1929,7 @@ static bool ShouldDiagnoseUnusedDecl(const NamedDecl *D) {
         if (!RD->hasTrivialDestructor() && !RD->hasAttr<WarnUnusedAttr>())
           return false;
 
-        if (const Expr *Init = VD->getInit()) {
-          if (const ExprWithCleanups *Cleanups =
-                  dyn_cast<ExprWithCleanups>(Init))
-            Init = Cleanups->getSubExpr();
+        if (Init) {
           const CXXConstructExpr *Construct =
             dyn_cast<CXXConstructExpr>(Init);
           if (Construct && !Construct->isElidable()) {
@@ -1931,10 +1941,16 @@ static bool ShouldDiagnoseUnusedDecl(const NamedDecl *D) {
 
           // Suppress the warning if we don't know how this is constructed, and
           // it could possibly be non-trivial constructor.
-          if (Init->isTypeDependent())
+          if (Init->isTypeDependent()) {
             for (const CXXConstructorDecl *Ctor : RD->ctors())
               if (!Ctor->isTrivial())
                 return false;
+          }
+
+          // Suppress the warning if the constructor is unresolved because
+          // its arguments are dependent.
+          if (isa<CXXUnresolvedConstructExpr>(Init))
+            return false;
         }
       }
     }
diff --git a/clang/test/SemaCXX/warn-unused-variables.cpp b/clang/test/SemaCXX/warn-unused-variables.cpp
index 2634fb1ec0f7f102bcce7be9bf035bb21b938504..4db8bdf12e5de09c99c0c4d98227da11ec6bcabd 100644
--- a/clang/test/SemaCXX/warn-unused-variables.cpp
+++ b/clang/test/SemaCXX/warn-unused-variables.cpp
@@ -154,13 +154,13 @@ namespace ctor_with_cleanups {
 
 #include "Inputs/warn-unused-variables.h"
 
-namespace arrayRecords {
-
 class NonTriviallyDestructible {
 public:
   ~NonTriviallyDestructible() {}
 };
 
+namespace arrayRecords {
+
 struct Foo {
   int x;
   Foo(int x) : x(x) {}
@@ -196,7 +196,7 @@ void test() {
   bar<2>();
 }
 
-}
+} // namespace arrayRecords
 
 #if __cplusplus >= 201103L
 namespace with_constexpr {
@@ -253,3 +253,44 @@ void foo(T &t) {
 }
 }
 #endif
+
+// Ensure we do not warn on lifetime extension
+namespace gh54489 {
+
+void f() {
+  const auto &a = NonTriviallyDestructible();
+  const auto &b = a; // expected-warning {{unused variable 'b'}}
+#if __cplusplus >= 201103L
+  const auto &&c = NonTriviallyDestructible();
+  auto &&d = c; // expected-warning {{unused variable 'd'}}
+#endif
+}
+
+struct S {
+  S() = default;
+  S(const S &) = default;
+  S(int);
+};
+
+template <typename T>
+void foo(T &t) {
+  const auto &extended = S{t};
+}
+
+void test_foo() {
+  int i;
+  foo(i);
+}
+
+struct RAIIWrapper {
+  RAIIWrapper();
+  ~RAIIWrapper();
+};
+
+void RAIIWrapperTest() {
+  auto const guard = RAIIWrapper();
+  auto const &guard2 = RAIIWrapper();
+  auto &&guard3 = RAIIWrapper();
+}
+
+} // namespace gh54489