diff --git a/gdb/build-id.c b/gdb/build-id.c
index 65b493f0832..f68384f0197 100644
--- a/gdb/build-id.c
+++ b/gdb/build-id.c
@@ -26,6 +26,7 @@
 #include "objfiles.h"
 #include "filenames.h"
 #include "gdbcore.h"
+#include "cli/cli-style.h"
 
 /* See build-id.h.  */
 
@@ -209,7 +210,7 @@ build_id_to_exec_bfd (size_t build_id_len, const bfd_byte *build_id)
 
 std::string
 find_separate_debug_file_by_buildid (struct objfile *objfile,
-				     std::vector<std::string> *warnings_vector)
+				     deferred_warnings *warnings)
 {
   const struct bfd_build_id *build_id;
 
@@ -228,12 +229,13 @@ find_separate_debug_file_by_buildid (struct objfile *objfile,
 	  && filename_cmp (bfd_get_filename (abfd.get ()),
 			   objfile_name (objfile)) == 0)
 	{
-	  std::string msg
-	    = string_printf (_("\"%s\": separate debug info file has no "
-			       "debug info"), bfd_get_filename (abfd.get ()));
 	  if (separate_debug_file_debug)
-	    gdb_printf (gdb_stdlog, "%s", msg.c_str ());
-	  warnings_vector->emplace_back (std::move (msg));
+	    gdb_printf (gdb_stdlog, "\"%s\": separate debug info file has no "
+			"debug info", bfd_get_filename (abfd.get ()));
+	  warnings->warn (_("\"%ps\": separate debug info file has no "
+			    "debug info"),
+			  styled_string (file_name_style.style (),
+					 bfd_get_filename (abfd.get ())));
 	}
       else if (abfd != NULL)
 	return std::string (bfd_get_filename (abfd.get ()));
diff --git a/gdb/build-id.h b/gdb/build-id.h
index 191720ddf28..a5c8b85a442 100644
--- a/gdb/build-id.h
+++ b/gdb/build-id.h
@@ -52,13 +52,13 @@ extern gdb_bfd_ref_ptr build_id_to_exec_bfd (size_t build_id_len,
    separate debug file, otherwise, return an empty string.
 
    Any warnings that are generated by the lookup process should be added to
-   WARNINGS_VECTOR, one std::string per warning.  If some other mechanism can
-   be used to lookup the debug information then the warning will not be shown,
-   however, if GDB fails to find suitable debug information using any
-   approach, then any warnings will be printed.  */
+   WARNINGS.  If some other mechanism can be used to lookup the debug
+   information then the warning will not be shown, however, if GDB fails to
+   find suitable debug information using any approach, then any warnings
+   will be printed.  */
 
 extern std::string find_separate_debug_file_by_buildid
-  (struct objfile *objfile, std::vector<std::string> *warnings_vector);
+  (struct objfile *objfile, deferred_warnings *warnings);
 
 /* Return an hex-string representation of BUILD_ID.  */
 
diff --git a/gdb/coffread.c b/gdb/coffread.c
index ff4d4ae5313..135ea3469c8 100644
--- a/gdb/coffread.c
+++ b/gdb/coffread.c
@@ -729,13 +729,13 @@ coff_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags)
   /* Try to add separate debug file if no symbols table found.   */
   if (!objfile->has_partial_symbols ())
     {
-      std::vector<std::string> warnings_vector;
+      deferred_warnings warnings;
       std::string debugfile
-	= find_separate_debug_file_by_buildid (objfile, &warnings_vector);
+	= find_separate_debug_file_by_buildid (objfile, &warnings);
 
       if (debugfile.empty ())
 	debugfile
-	  = find_separate_debug_file_by_debuglink (objfile, &warnings_vector);
+	  = find_separate_debug_file_by_debuglink (objfile, &warnings);
 
       if (!debugfile.empty ())
 	{
@@ -746,9 +746,8 @@ coff_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags)
 	}
       /* If all the methods to collect the debuginfo failed, print any
 	 warnings that were collected.  */
-      if (debugfile.empty () && !warnings_vector.empty ())
-	for (const std::string &w : warnings_vector)
-	  warning ("%s", w.c_str ());
+      if (debugfile.empty () && ! warnings.empty ())
+	warnings.emit ();
     }
 }
 
diff --git a/gdb/elfread.c b/gdb/elfread.c
index 799e3b914f8..7215fc4f35b 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -1224,14 +1224,13 @@ elf_symfile_read_dwarf2 (struct objfile *objfile,
 	   && objfile->separate_debug_objfile == NULL
 	   && objfile->separate_debug_objfile_backlink == NULL)
     {
-      std::vector<std::string> warnings_vector;
+      deferred_warnings warnings;
 
       std::string debugfile
-	= find_separate_debug_file_by_buildid (objfile, &warnings_vector);
+	= find_separate_debug_file_by_buildid (objfile, &warnings);
 
       if (debugfile.empty ())
-	debugfile = find_separate_debug_file_by_debuglink (objfile,
-							   &warnings_vector);
+	debugfile = find_separate_debug_file_by_debuglink (objfile, &warnings);
 
       if (!debugfile.empty ())
 	{
@@ -1274,11 +1273,10 @@ elf_symfile_read_dwarf2 (struct objfile *objfile,
 		}
 	    }
 	}
-      /* If all the methods to collect the debuginfo failed, print
-	 the warnings, if there're any. */
-      if (debugfile.empty () && !has_dwarf2 && !warnings_vector.empty ())
-	for (const std::string &w : warnings_vector)
-	  warning ("%s", w.c_str ());
+      /* If all the methods to collect the debuginfo failed, print the
+	 warnings, if there are any. */
+      if (debugfile.empty () && !has_dwarf2 && !warnings.empty ())
+	warnings.emit ();
     }
 
   return has_dwarf2;
diff --git a/gdb/symfile.c b/gdb/symfile.c
index 3a0aee163ff..0ccaf15e4f6 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -1243,7 +1243,7 @@ bool separate_debug_file_debug = false;
 static int
 separate_debug_file_exists (const std::string &name, unsigned long crc,
 			    struct objfile *parent_objfile,
-			    std::vector<std::string> *warnings_vector)
+			    deferred_warnings *warnings)
 {
   unsigned long file_crc;
   int file_crc_p;
@@ -1336,13 +1336,16 @@ separate_debug_file_exists (const std::string &name, unsigned long crc,
 
       if (verified_as_different || parent_crc != file_crc)
 	{
-	  std::string msg
-	    = string_printf (_("the debug information found in \"%s\""
-			       " does not match \"%s\" (CRC mismatch).\n"),
-			     name.c_str (), objfile_name (parent_objfile));
 	  if (separate_debug_file_debug)
-	    gdb_printf (gdb_stdlog, "%s", msg.c_str ());
-	  warnings_vector->emplace_back (std::move (msg));
+	    gdb_printf (gdb_stdlog, "the debug information found in \"%s\""
+			" does not match \"%s\" (CRC mismatch).\n",
+			name.c_str (), objfile_name (parent_objfile));
+	  warnings->warn (_("the debug information found in \"%ps\""
+			    " does not match \"%ps\" (CRC mismatch)."),
+			  styled_string (file_name_style.style (),
+					 name.c_str ()),
+			  styled_string (file_name_style.style (),
+					 objfile_name (parent_objfile)));
 	}
 
       return 0;
@@ -1385,7 +1388,7 @@ find_separate_debug_file (const char *dir,
 			  const char *canon_dir,
 			  const char *debuglink,
 			  unsigned long crc32, struct objfile *objfile,
-			  std::vector<std::string> *warnings_vector)
+			  deferred_warnings *warnings)
 {
   if (separate_debug_file_debug)
     gdb_printf (gdb_stdlog,
@@ -1396,7 +1399,7 @@ find_separate_debug_file (const char *dir,
   std::string debugfile = dir;
   debugfile += debuglink;
 
-  if (separate_debug_file_exists (debugfile, crc32, objfile, warnings_vector))
+  if (separate_debug_file_exists (debugfile, crc32, objfile, warnings))
     return debugfile;
 
   /* Then try in the subdirectory named DEBUG_SUBDIRECTORY.  */
@@ -1405,7 +1408,7 @@ find_separate_debug_file (const char *dir,
   debugfile += "/";
   debugfile += debuglink;
 
-  if (separate_debug_file_exists (debugfile, crc32, objfile, warnings_vector))
+  if (separate_debug_file_exists (debugfile, crc32, objfile, warnings))
     return debugfile;
 
   /* Then try in the global debugfile directories.
@@ -1450,8 +1453,7 @@ find_separate_debug_file (const char *dir,
       debugfile += dir_notarget;
       debugfile += debuglink;
 
-      if (separate_debug_file_exists (debugfile, crc32, objfile,
-				      warnings_vector))
+      if (separate_debug_file_exists (debugfile, crc32, objfile, warnings))
 	return debugfile;
 
       const char *base_path = NULL;
@@ -1473,8 +1475,7 @@ find_separate_debug_file (const char *dir,
 	  debugfile += "/";
 	  debugfile += debuglink;
 
-	  if (separate_debug_file_exists (debugfile, crc32, objfile,
-					  warnings_vector))
+	  if (separate_debug_file_exists (debugfile, crc32, objfile, warnings))
 	    return debugfile;
 
 	  /* If the file is in the sysroot, try using its base path in
@@ -1501,7 +1502,7 @@ find_separate_debug_file (const char *dir,
 	      debugfile += debuglink;
 
 	      if (separate_debug_file_exists (debugfile, crc32, objfile,
-					      warnings_vector))
+					      warnings))
 		return debugfile;
 	    }
 	}
@@ -1533,7 +1534,7 @@ terminate_after_last_dir_separator (char *path)
 
 std::string
 find_separate_debug_file_by_debuglink
-  (struct objfile *objfile, std::vector<std::string> *warnings_vector)
+  (struct objfile *objfile, deferred_warnings *warnings)
 {
   uint32_t crc32;
 
@@ -1554,7 +1555,7 @@ find_separate_debug_file_by_debuglink
   std::string debugfile
     = find_separate_debug_file (dir.c_str (), canon_dir.get (),
 				debuglink.get (), crc32, objfile,
-				warnings_vector);
+				warnings);
 
   if (debugfile.empty ())
     {
@@ -1579,7 +1580,7 @@ find_separate_debug_file_by_debuglink
 							debuglink.get (),
 							crc32,
 							objfile,
-							warnings_vector);
+							warnings);
 		}
 	    }
 	}
diff --git a/gdb/symfile.h b/gdb/symfile.h
index 7c800ea6cf9..296fee945c1 100644
--- a/gdb/symfile.h
+++ b/gdb/symfile.h
@@ -244,11 +244,14 @@ extern void symbol_file_add_separate (const gdb_bfd_ref_ptr &, const char *,
 /* Find separate debuginfo for OBJFILE (using .gnu_debuglink section).
    Returns pathname, or an empty string.
 
-   Any warnings generated as part of this lookup are added to
-   WARNINGS_VECTOR, one std::string per warning.  */
+   Any warnings generated as part of this lookup are added to WARNINGS.  If
+   some other mechanism can be used to lookup the debug information then
+   the warning will not be shown, however, if GDB fails to find suitable
+   debug information using any approach, then any warnings will be
+   printed.  */
 
 extern std::string find_separate_debug_file_by_debuglink
-  (struct objfile *objfile, std::vector<std::string> *warnings_vector);
+  (struct objfile *objfile, deferred_warnings *warnings);
 
 /* Build (allocate and populate) a section_addr_info struct from an
    existing section table.  */
diff --git a/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp b/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp
index b49770f4a5e..148363a7c05 100644
--- a/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp
+++ b/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp
@@ -95,6 +95,30 @@ gdb_test "file ${build_id_debug_file}" \
 	 "\\(No debugging symbols found in \[^\r\n\]+\\)"] \
     "load test file, expect a warning"
 
+# Do the same thing again, but this time check that the styling is
+# correct.
+with_test_prefix "check styling" {
+    save_vars { env(TERM) } {
+	# We need an ANSI-capable terminal to get styled output.
+	setenv TERM ansi
+
+	clean_restart
+
+	gdb_test_no_output "set debug-file-directory ${debug_file_directory}" \
+	    "set debug-file-directory"
+
+	# Now load the file into GDB, and look for the warning.
+	set debug_file_re [style [string_to_regexp $build_id_debug_file] file]
+
+	gdb_test "file ${build_id_debug_file}" \
+	    [multi_line \
+		 "Reading symbols from $debug_file_re\\.\\.\\." \
+		 "warning: \"$debug_file_re\": separate debug info file has no debug info" \
+		 "\\(No debugging symbols found in \[^\r\n\]+\\)"] \
+	    "load test file, expect a warning"
+    }
+}
+
 # Now we should close GDB.
 gdb_exit
 
diff --git a/gdb/testsuite/gdb.debuginfod/crc_mismatch.exp b/gdb/testsuite/gdb.debuginfod/crc_mismatch.exp
index 588fb7e7277..074028eadfd 100644
--- a/gdb/testsuite/gdb.debuginfod/crc_mismatch.exp
+++ b/gdb/testsuite/gdb.debuginfod/crc_mismatch.exp
@@ -58,12 +58,39 @@ if {[build_executable crc_mismatch.exp crc_mismatch-2 crc_mismatch-2.c debug] !=
     # to trigger the "CRC mismatch" warning.
     remote_exec build "mv ${debugfile} [standard_output_file crc_mismatch-2.debug]"
 
-    gdb_exit
-    gdb_start
+    clean_restart
 
     set escapedobjdirsubdir [string_to_regexp [standard_output_file {}]]
+    gdb_test "file [standard_output_file crc_mismatch-2]" \
+	[multi_line \
+	     "Reading symbols from ${escapedobjdirsubdir}/crc_mismatch-2\\.\\.\\." \
+	     "warning: the debug information found in \"${escapedobjdirsubdir}/crc_mismatch-2\\.debug\" does not match \"${escapedobjdirsubdir}/crc_mismatch-2\" \\(CRC mismatch\\)\\." \
+	     "\\(No debugging symbols found in .*\\)"] \
+	"CRC mismatch is reported"
+
+    # Do the same thing again, but this time check that the styling is
+    # correct.
+    with_test_prefix "check styling" {
+	save_vars { env(TERM) } {
+	    # We need an ANSI-capable terminal to get styled output.
+	    setenv TERM ansi
+
+	    clean_restart
+
+	    set debug_file_re [style "${escapedobjdirsubdir}/crc_mismatch-2\\.debug" file]
+	    set exe_file_re [style "${escapedobjdirsubdir}/crc_mismatch-2" file]
+
+	    gdb_test "file [standard_output_file crc_mismatch-2]" \
+		[multi_line \
+		     "Reading symbols from ${exe_file_re}\\.\\.\\." \
+		     "warning: the debug information found in \"${debug_file_re}\" does not match \"${exe_file_re}\" \\(CRC mismatch\\)\\." \
+		     "\\(No debugging symbols found in \[^\r\n\]+\\)"] \
+		"CRC mismatch is reported"
+	}
+    }
 
-    gdb_test "file [standard_output_file crc_mismatch-2]" "warning: the debug information found in \"${escapedobjdirsubdir}/crc_mismatch-2\\.debug\" does not match \"${escapedobjdirsubdir}/crc_mismatch-2\" \\(CRC mismatch\\)\\..*\\(No debugging symbols found in .*\\).*" "CRC mismatch is reported"
+    # Restart GDB with styling disabled.
+    clean_restart
 }
 
 # Create CACHE and DB directories ready for debuginfod to use.
diff --git a/gdb/ui-file.h b/gdb/ui-file.h
index de24620e247..31f87ffd51d 100644
--- a/gdb/ui-file.h
+++ b/gdb/ui-file.h
@@ -30,6 +30,8 @@ class ui_file
   ui_file ();
   virtual ~ui_file () = 0;
 
+  ui_file (ui_file &&other) = default;
+
   /* Public non-virtual API.  */
 
   void printf (const char *, ...) ATTRIBUTE_PRINTF (2, 3);
@@ -177,6 +179,8 @@ class string_file : public ui_file
   explicit string_file (bool term_out) : m_term_out (term_out) {};
   ~string_file () override;
 
+  string_file (string_file &&other) = default;
+
   /* Override ui_file methods.  */
 
   void write (const char *buf, long length_buf) override;
diff --git a/gdb/utils.h b/gdb/utils.h
index 3faac20ec0f..602216a4c03 100644
--- a/gdb/utils.h
+++ b/gdb/utils.h
@@ -364,4 +364,62 @@ assign_return_if_changed (T &lval, const T &val)
   return true;
 }
 
+/* In some cases GDB needs to try several different solutions to a problem,
+   if any of the solutions work then as far as the user is concerned the
+   problem is solved, and GDB should continue without warnings.  However,
+   if non of the solutions work then GDB should emit any warnings that
+   occurred while trying each possible solution.
+
+   One example of this is locating separate debug info.  There are several
+   different approaches for this; following the .gnu_debuglink, a build-id
+   based lookup, or using debuginfod.  If any works, and debug info is
+   located, then the user doesn't want to see warnings from the earlier
+   approaches that were tried and failed.
+
+   However, GDB should emit all the warnings using separate calls to
+   warning -- this ensures that each warning is formatted on its own line,
+   and that any styling is emitted correctly.
+
+   This class helps with deferring warnings.  Warnings can be added to an
+   instance of this class with the 'warn' function, and all warnings can be
+   emitted with a single call to 'emit'.  */
+
+struct deferred_warnings
+{
+  /* Add a warning to the list of deferred warnings.  */
+  void warn (const char *format, ...)
+  {
+    /* Generate the warning text into a string_file.  We allow the text to
+       be styled only if gdb_stderr allows styling -- warnings are sent to
+       gdb_stderr.  */
+    string_file msg (gdb_stderr->can_emit_style_escape ());
+
+    va_list args;
+    va_start (args, format);
+    msg.vprintf (format, args);
+    va_end (args);
+
+    /* Move the text into the list of deferred warnings.  */
+    m_warnings.emplace_back (std::move (msg));
+  }
+
+  /* Emit all warnings.  */
+  void emit () const
+  {
+    for (const auto &w : m_warnings)
+      warning ("%s", w.c_str ());
+  }
+
+  /* Return true if there are no deferred warnings.  */
+  bool empty () const
+  {
+    return m_warnings.empty ();
+  }
+
+private:
+
+  /* The list of all deferred warnings.  */
+  std::vector<string_file> m_warnings;
+};
+
 #endif /* UTILS_H */
