[flang-commits] [clang] [clang-tools-extra] [flang] [lldb] [Clang] Refactor and consolidate color diagnostic handling (PR #202441)

Joseph Huber via flang-commits flang-commits at lists.llvm.org
Tue Jun 23 11:58:16 PDT 2026


https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/202441

>From d05babe55a51c9d93be79e501fbb386e84e925c4 Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Tue, 23 Jun 2026 13:54:08 -0500
Subject: [PATCH] [Clang] Refactor and consolidate color diagnostic handling

Summary:
This PR tries to consolidate the color output handling in Clang. The
motivation was noticing that `-Xclang -ast-dump` would not behave like
`-fcolor-diagnostics` and would output ANSI codes to a file when I tried
to pipe it.

This PR primarily turns the handling into a tri-state enum keyed off of
`-f[no]-color-diagnostics`. The default/auto case will be if the target
stream supports colors. Getting this to work required a lot of seemingly
unrelated plumbing.

Co-authored-by: Cursor <cursoragent at cursor.com>
---
 clang-tools-extra/clang-tidy/ClangTidy.cpp    |  9 +++--
 clang-tools-extra/clangd/Compiler.cpp         |  2 +-
 .../include/clang/Basic/DiagnosticOptions.def |  2 +-
 clang/include/clang/Basic/DiagnosticOptions.h | 23 +++++++++++
 clang/include/clang/Options/Options.td        |  2 +-
 clang/lib/AST/ASTDumper.cpp                   | 28 +++++++------
 clang/lib/Basic/Warnings.cpp                  |  6 ++-
 clang/lib/Driver/ToolChains/CommonArgs.cpp    | 10 ++++-
 clang/lib/Frontend/CompilerInvocation.cpp     | 39 ++++++++++---------
 clang/lib/Frontend/FrontendActions.cpp        |  3 +-
 clang/lib/Frontend/TextDiagnostic.cpp         | 23 +++++------
 clang/lib/Frontend/TextDiagnosticPrinter.cpp  |  5 ++-
 .../Misc/diagnostic-color-output-stream.cpp   | 25 ++++++++++++
 clang/unittests/Tooling/ToolingTest.cpp       |  8 ++--
 flang/lib/Frontend/CompilerInvocation.cpp     |  9 +++--
 flang/lib/Frontend/TextDiagnosticPrinter.cpp  |  8 ++--
 flang/test/Driver/color-diagnostics.f90       |  3 +-
 .../TypeSystem/Clang/TypeSystemClang.cpp      | 13 +++++--
 18 files changed, 151 insertions(+), 67 deletions(-)
 create mode 100644 clang/test/Misc/diagnostic-color-output-stream.cpp

diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index 05c8fd02fe86a..24cbd0940226d 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -105,10 +105,13 @@ class ErrorReporter {
         DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), DiagOpts)),
         Diags(DiagnosticIDs::create(), DiagOpts, DiagPrinter),
         SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes) {
-    DiagOpts.ShowColors = Context.getOptions().UseColor.value_or(
-        llvm::sys::Process::StandardOutHasColors());
+    DiagOpts.setShowColors(Context.getOptions().UseColor.value_or(
+                               llvm::sys::Process::StandardOutHasColors())
+                               ? ShowColorsKind::On
+                               : ShowColorsKind::Off);
     DiagPrinter->BeginSourceFile(LangOpts);
-    if (DiagOpts.ShowColors && !llvm::sys::Process::StandardOutIsDisplayed())
+    if (DiagOpts.showColors(llvm::sys::Process::StandardOutHasColors()) &&
+        !llvm::sys::Process::StandardOutIsDisplayed())
       llvm::sys::Process::UseANSIEscapeCodes(true);
   }
 
diff --git a/clang-tools-extra/clangd/Compiler.cpp b/clang-tools-extra/clangd/Compiler.cpp
index 9ea7df139382a..4644cd75c0833 100644
--- a/clang-tools-extra/clangd/Compiler.cpp
+++ b/clang-tools-extra/clangd/Compiler.cpp
@@ -49,7 +49,7 @@ void disableUnsupportedOptions(CompilerInvocation &CI) {
   // our compiler invocation set-up doesn't seem to work with it (leading
   // assertions in VerifyDiagnosticConsumer).
   CI.getDiagnosticOpts().VerifyDiagnostics = false;
-  CI.getDiagnosticOpts().ShowColors = false;
+  CI.getDiagnosticOpts().setShowColors(ShowColorsKind::Off);
 
   // Disable any dependency outputting, we don't want to generate files or write
   // to stdout/stderr.
diff --git a/clang/include/clang/Basic/DiagnosticOptions.def b/clang/include/clang/Basic/DiagnosticOptions.def
index 17d518c2b7fdd..764e2f1fedbdd 100644
--- a/clang/include/clang/Basic/DiagnosticOptions.def
+++ b/clang/include/clang/Basic/DiagnosticOptions.def
@@ -65,7 +65,7 @@ VALUE_DIAGOPT(ShowCategories, 2, 0) /// Show categories: 0 -> none, 1 -> Number,
 
 ENUM_DIAGOPT(Format, TextDiagnosticFormat, 2, Clang) /// Format for diagnostics:
 
-DIAGOPT(ShowColors, 1, 0)       /// Show diagnostics with ANSI color sequences.
+ENUM_DIAGOPT(ShowColors, ShowColorsKind, 2, ShowColorsKind::Auto)
 DIAGOPT(UseANSIEscapeCodes, 1, 0)
 ENUM_DIAGOPT(ShowOverloads, OverloadsShown, 1,
              Ovl_All)    /// Overload candidates to show.
diff --git a/clang/include/clang/Basic/DiagnosticOptions.h b/clang/include/clang/Basic/DiagnosticOptions.h
index a230022224de5..4cfc636619883 100644
--- a/clang/include/clang/Basic/DiagnosticOptions.h
+++ b/clang/include/clang/Basic/DiagnosticOptions.h
@@ -23,6 +23,16 @@ class ArgList;
 namespace clang {
 class DiagnosticsEngine;
 
+/// Controls whether to show colors in diagnostic output.
+enum class ShowColorsKind : unsigned {
+  /// Emit colors only if the output stream is detected to support them.
+  Auto,
+  /// Always emit colors regardless of the output stream.
+  On,
+  /// Never emit colors regardless of the output stream.
+  Off,
+};
+
 /// Specifies which overload candidates to display when overload
 /// resolution fails.
 enum OverloadsShown : unsigned {
@@ -143,6 +153,19 @@ class DiagnosticOptions {
 #define ENUM_DIAGOPT(Name, Type, Bits, Default) set##Name(Default);
 #include "clang/Basic/DiagnosticOptions.def"
   }
+
+  /// Resolve the color mode against a stream's capability.
+  bool showColors(bool StreamHasColors) const {
+    switch (getShowColors()) {
+    case ShowColorsKind::On:
+      return true;
+    case ShowColorsKind::Off:
+      return false;
+    case ShowColorsKind::Auto:
+      return StreamHasColors;
+    }
+    return false;
+  }
 };
 
 using TextDiagnosticFormat = DiagnosticOptions::TextDiagnosticFormat;
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 4fc9f4d4c3472..2815d3a322703 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2093,7 +2093,7 @@ def fcolor_diagnostics : Flag<["-"], "fcolor-diagnostics">, Group<f_Group>,
   Visibility<[ClangOption, CLOption, DXCOption, CC1Option, FlangOption, FC1Option]>,
   HelpText<"Enable colors in diagnostics">;
 def fno_color_diagnostics : Flag<["-"], "fno-color-diagnostics">, Group<f_Group>,
-  Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>,
+  Visibility<[ClangOption, CLOption, DXCOption, CC1Option, FlangOption, FC1Option]>,
   HelpText<"Disable colors in diagnostics">;
 def : Flag<["-"], "fdiagnostics-color">, Group<f_Group>,
   Visibility<[ClangOption, CLOption, DXCOption, FlangOption]>,
diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp
index b3071d83ed51f..146fc5ea5caad 100644
--- a/clang/lib/AST/ASTDumper.cpp
+++ b/clang/lib/AST/ASTDumper.cpp
@@ -16,12 +16,18 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclLookups.h"
 #include "clang/AST/JSONNodeDumper.h"
+#include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/SourceManager.h"
 #include "llvm/Support/raw_ostream.h"
 
 using namespace clang;
 using namespace clang::comments;
 
+static bool showColorsForStream(const ASTContext &Ctx, raw_ostream &OS) {
+  return Ctx.getDiagnostics().getDiagnosticOptions().showColors(
+      OS.has_colors());
+}
+
 void ASTDumper::dumpInvalidDeclContext(const DeclContext *DC) {
   NodeDumper.AddChild([=] {
     if (!DC) {
@@ -189,7 +195,7 @@ LLVM_DUMP_METHOD void QualType::dump() const {
 
 LLVM_DUMP_METHOD void QualType::dump(llvm::raw_ostream &OS,
                                      const ASTContext &Context) const {
-  ASTDumper Dumper(OS, Context, Context.getDiagnostics().getShowColors());
+  ASTDumper Dumper(OS, Context, showColorsForStream(Context, OS));
   Dumper.Visit(*this);
 }
 
@@ -210,7 +216,7 @@ LLVM_DUMP_METHOD void TypeLoc::dump() const {
 
 LLVM_DUMP_METHOD void TypeLoc::dump(llvm::raw_ostream &OS,
                                     const ASTContext &Context) const {
-  ASTDumper(OS, Context, Context.getDiagnostics().getShowColors()).Visit(*this);
+  ASTDumper(OS, Context, showColorsForStream(Context, OS)).Visit(*this);
 }
 
 //===----------------------------------------------------------------------===//
@@ -230,7 +236,7 @@ LLVM_DUMP_METHOD void Decl::dump(raw_ostream &OS, bool Deserialize,
     (void)Deserialize; // FIXME?
     P.Visit(this);
   } else {
-    ASTDumper P(OS, Ctx, Ctx.getDiagnostics().getShowColors());
+    ASTDumper P(OS, Ctx, showColorsForStream(Ctx, OS));
     P.setDeserialize(Deserialize);
     P.Visit(this);
   }
@@ -261,7 +267,7 @@ LLVM_DUMP_METHOD void DeclContext::dumpAsDecl(const ASTContext *Ctx) const {
     // If an ASTContext is not available, a less capable ASTDumper is
     // constructed for which color diagnostics are, regrettably, disabled.
     ASTDumper P = Ctx ? ASTDumper(llvm::errs(), *Ctx,
-                                  Ctx->getDiagnostics().getShowColors())
+                                  showColorsForStream(*Ctx, llvm::errs()))
                       : ASTDumper(llvm::errs(), /*ShowColors*/ false);
     P.dumpInvalidDeclContext(this);
   }
@@ -278,7 +284,7 @@ LLVM_DUMP_METHOD void DeclContext::dumpLookups(raw_ostream &OS,
   while (!DC->isTranslationUnit())
     DC = DC->getParent();
   const ASTContext &Ctx = cast<TranslationUnitDecl>(DC)->getASTContext();
-  ASTDumper P(OS, Ctx, Ctx.getDiagnostics().getShowColors());
+  ASTDumper P(OS, Ctx, showColorsForStream(Ctx, OS));
   P.setDeserialize(Deserialize);
   P.dumpLookups(this, DumpDecls);
 }
@@ -294,7 +300,7 @@ LLVM_DUMP_METHOD void Stmt::dump() const {
 
 LLVM_DUMP_METHOD void Stmt::dump(raw_ostream &OS,
                                  const ASTContext &Context) const {
-  ASTDumper P(OS, Context, Context.getDiagnostics().getShowColors());
+  ASTDumper P(OS, Context, showColorsForStream(Context, OS));
   P.Visit(this);
 }
 
@@ -320,7 +326,7 @@ LLVM_DUMP_METHOD void Comment::dump(raw_ostream &OS,
   const auto *FC = dyn_cast<FullComment>(this);
   if (!FC)
     return;
-  ASTDumper Dumper(OS, Context, Context.getDiagnostics().getShowColors());
+  ASTDumper Dumper(OS, Context, showColorsForStream(Context, OS));
   Dumper.Visit(FC, FC);
 }
 
@@ -343,7 +349,7 @@ LLVM_DUMP_METHOD void APValue::dump() const {
 
 LLVM_DUMP_METHOD void APValue::dump(raw_ostream &OS,
                                     const ASTContext &Context) const {
-  ASTDumper Dumper(OS, Context, Context.getDiagnostics().getShowColors());
+  ASTDumper Dumper(OS, Context, showColorsForStream(Context, OS));
   Dumper.Visit(*this, /*Ty=*/Context.getPointerType(Context.CharTy));
 }
 
@@ -357,7 +363,7 @@ LLVM_DUMP_METHOD void ConceptReference::dump() const {
 
 LLVM_DUMP_METHOD void ConceptReference::dump(raw_ostream &OS) const {
   auto &Ctx = getNamedConcept()->getASTContext();
-  ASTDumper P(OS, Ctx, Ctx.getDiagnostics().getShowColors());
+  ASTDumper P(OS, Ctx, showColorsForStream(Ctx, OS));
   P.Visit(this);
 }
 
@@ -376,7 +382,7 @@ LLVM_DUMP_METHOD void TemplateName::dump() const {
 
 LLVM_DUMP_METHOD void TemplateName::dump(llvm::raw_ostream &OS,
                                          const ASTContext &Context) const {
-  ASTDumper Dumper(OS, Context, Context.getDiagnostics().getShowColors());
+  ASTDumper Dumper(OS, Context, showColorsForStream(Context, OS));
   Dumper.Visit(*this);
 }
 
@@ -391,6 +397,6 @@ LLVM_DUMP_METHOD void TemplateArgument::dump() const {
 
 LLVM_DUMP_METHOD void TemplateArgument::dump(llvm::raw_ostream &OS,
                                              const ASTContext &Context) const {
-  ASTDumper Dumper(OS, Context, Context.getDiagnostics().getShowColors());
+  ASTDumper Dumper(OS, Context, showColorsForStream(Context, OS));
   Dumper.Visit(*this);
 }
diff --git a/clang/lib/Basic/Warnings.cpp b/clang/lib/Basic/Warnings.cpp
index 5f48e0ec81554..c76ef7a5912e4 100644
--- a/clang/lib/Basic/Warnings.cpp
+++ b/clang/lib/Basic/Warnings.cpp
@@ -28,6 +28,7 @@
 #include "clang/Basic/DiagnosticOptions.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/VirtualFileSystem.h"
+#include "llvm/Support/raw_ostream.h"
 #include <cstring>
 using namespace clang;
 
@@ -53,7 +54,10 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
 
   Diags.setElideType(Opts.ElideType);
   Diags.setPrintTemplateTree(Opts.ShowTemplateTree);
-  Diags.setShowColors(Opts.ShowColors);
+  // This flag bakes colors into the formatted message (e.g. template diffs), so
+  // resolve 'auto' against the default diagnostic stream (stderr) rather than
+  // forcing colors on. This keeps ANSI codes out of redirected or logged output.
+  Diags.setShowColors(Opts.showColors(llvm::errs().has_colors()));
 
   // Handle -ferror-limit
   if (Opts.ErrorLimit)
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index ba4341ed41f1a..dfd13fbe8a4eb 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -3317,8 +3317,16 @@ void tools::handleColorDiagnosticsArgs(const Driver &D, const ArgList &Args,
           << Value << A->getOption().getName();
   }
 
-  if (D.getDiags().getDiagnosticOptions().ShowColors)
+  switch (D.getDiags().getDiagnosticOptions().getShowColors()) {
+  case ShowColorsKind::On:
     CmdArgs.push_back("-fcolor-diagnostics");
+    break;
+  case ShowColorsKind::Off:
+    CmdArgs.push_back("-fno-color-diagnostics");
+    break;
+  case ShowColorsKind::Auto:
+    break;
+  }
 }
 
 void tools::escapeSpacesAndBackslashes(const char *Arg,
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 55b344fc2da26..52d40f70079e4 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -2485,35 +2485,31 @@ static bool ParseDependencyOutputArgs(DependencyOutputOptions &Opts,
   return Diags.getNumErrors() == NumErrorsBefore;
 }
 
-static bool parseShowColorsArgs(const ArgList &Args, bool DefaultColor) {
+static ShowColorsKind parseShowColorsMode(const ArgList &Args,
+                                          bool DefaultColor) {
   // Color diagnostics default to auto ("on" if terminal supports) in the driver
   // but default to off in cc1, needing an explicit OPT_fdiagnostics_color.
   // Support both clang's -f[no-]color-diagnostics and gcc's
   // -f[no-]diagnostics-colors[=never|always|auto].
-  enum {
-    Colors_On,
-    Colors_Off,
-    Colors_Auto
-  } ShowColors = DefaultColor ? Colors_Auto : Colors_Off;
+  ShowColorsKind Mode =
+      DefaultColor ? ShowColorsKind::Auto : ShowColorsKind::Off;
   for (auto *A : Args) {
     const Option &O = A->getOption();
     if (O.matches(options::OPT_fcolor_diagnostics)) {
-      ShowColors = Colors_On;
+      Mode = ShowColorsKind::On;
     } else if (O.matches(options::OPT_fno_color_diagnostics)) {
-      ShowColors = Colors_Off;
+      Mode = ShowColorsKind::Off;
     } else if (O.matches(options::OPT_fdiagnostics_color_EQ)) {
       StringRef Value(A->getValue());
       if (Value == "always")
-        ShowColors = Colors_On;
+        Mode = ShowColorsKind::On;
       else if (Value == "never")
-        ShowColors = Colors_Off;
+        Mode = ShowColorsKind::Off;
       else if (Value == "auto")
-        ShowColors = Colors_Auto;
+        Mode = ShowColorsKind::Auto;
     }
   }
-  return ShowColors == Colors_On ||
-         (ShowColors == Colors_Auto &&
-          llvm::sys::Process::StandardErrHasColors());
+  return Mode;
 }
 
 static bool checkVerifyPrefixes(const std::vector<std::string> &VerifyPrefixes,
@@ -2594,8 +2590,16 @@ void CompilerInvocationBase::GenerateDiagnosticArgs(
     GenerateArg(Consumer, OPT_diagnostic_serialized_file,
                 Opts.DiagnosticSerializationFile);
 
-  if (Opts.ShowColors)
+  switch (Opts.getShowColors()) {
+  case ShowColorsKind::On:
     GenerateArg(Consumer, OPT_fcolor_diagnostics);
+    break;
+  case ShowColorsKind::Off:
+    GenerateArg(Consumer, OPT_fno_color_diagnostics);
+    break;
+  case ShowColorsKind::Auto:
+    break;
+  }
 
   if (Opts.VerifyDiagnostics &&
       llvm::is_contained(Opts.VerifyPrefixes, "expected"))
@@ -2704,7 +2708,7 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
   if (Arg *A =
           Args.getLastArg(OPT_diagnostic_serialized_file, OPT__serialize_diags))
     Opts.DiagnosticSerializationFile = A->getValue();
-  Opts.ShowColors = parseShowColorsArgs(Args, DefaultDiagColor);
+  Opts.setShowColors(parseShowColorsMode(Args, DefaultDiagColor));
 
   Opts.VerifyDiagnostics = Args.hasArg(OPT_verify) || Args.hasArg(OPT_verify_EQ);
   Opts.VerifyDirectives = Args.hasArg(OPT_verify_directives);
@@ -5116,8 +5120,7 @@ bool CompilerInvocation::CreateFromArgsImpl(
   ParseMigratorArgs(Res.getMigratorOpts(), Args, Diags);
   ParseAnalyzerArgs(Res.getAnalyzerOpts(), Args, Diags);
   ParseSSAFArgs(Res.getSSAFOpts(), Args, Diags);
-  ParseDiagnosticArgs(Res.getDiagnosticOpts(), Args, &Diags,
-                      /*DefaultDiagColor=*/false);
+  ParseDiagnosticArgs(Res.getDiagnosticOpts(), Args, &Diags);
   ParseFrontendArgs(Res.getFrontendOpts(), Args, Diags, LangOpts.IsHeaderFile);
   // FIXME: We shouldn't have to pass the DashX option around here
   InputKind DashX = Res.getFrontendOpts().DashX;
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index ba3487d52e380..f3fce25b78a8c 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -700,7 +700,8 @@ namespace {
       Out.indent(2) << "Diagnostic options:\n";
 #define DIAGOPT(Name, Bits, Default) DUMP_BOOLEAN(DiagOpts.Name, #Name);
 #define ENUM_DIAGOPT(Name, Type, Bits, Default)                              \
-    Out.indent(4) << #Name << ": " << DiagOpts.get##Name() << "\n";
+    Out.indent(4) << #Name << ": "                                             \
+                  << static_cast<unsigned>(DiagOpts.get##Name()) << "\n";
 #define VALUE_DIAGOPT(Name, Bits, Default)                                   \
     Out.indent(4) << #Name << ": " << DiagOpts.Name << "\n";
 #include "clang/Basic/DiagnosticOptions.def"
diff --git a/clang/lib/Frontend/TextDiagnostic.cpp b/clang/lib/Frontend/TextDiagnostic.cpp
index 3f30709b0447e..01a4d2f6392d3 100644
--- a/clang/lib/Frontend/TextDiagnostic.cpp
+++ b/clang/lib/Frontend/TextDiagnostic.cpp
@@ -730,15 +730,16 @@ void TextDiagnostic::emitDiagnosticMessage(
   if (Loc.isValid())
     emitDiagnosticLoc(Loc, PLoc, Level, Ranges);
 
-  if (DiagOpts.ShowColors)
+  if (DiagOpts.showColors(OS.has_colors()))
     OS.resetColor();
 
   if (DiagOpts.ShowLevel)
-    printDiagnosticLevel(OS, Level, DiagOpts.ShowColors);
+    printDiagnosticLevel(OS, Level, DiagOpts.showColors(OS.has_colors()));
   printDiagnosticMessage(OS,
                          /*IsSupplemental*/ Level == DiagnosticsEngine::Note,
                          Message, OS.getColumn() - StartOfLocationInfo,
-                         DiagOpts.MessageLength, DiagOpts.ShowColors);
+                         DiagOpts.MessageLength,
+                         DiagOpts.showColors(OS.has_colors()));
   // We use a formatted ostream, which does its own buffering. Flush here
   // so we keep the proper order of output.
   OS.flush();
@@ -872,7 +873,7 @@ void TextDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
   if (!DiagOpts.ShowLocation)
     return;
 
-  if (DiagOpts.ShowColors)
+  if (DiagOpts.showColors(OS.has_colors()))
     OS.changeColor(SavedColor, true);
 
   emitFilename(PLoc.getFilename(), Loc.getManager());
@@ -1429,7 +1430,7 @@ void TextDiagnostic::emitSnippetAndCaret(
   // emit, starting from the first line.
   std::unique_ptr<SmallVector<StyleRange>[]> SourceStyles =
       highlightLines(BufData, Lines.first, Lines.second, PP, LangOpts,
-                     DiagOpts.ShowColors, FID, SM);
+                     DiagOpts.showColors(OS.has_colors()), FID, SM);
 
   SmallVector<LineRange> LineRanges =
       prepareAndFilterRanges(Ranges, SM, Lines, FID, LangOpts);
@@ -1508,22 +1509,22 @@ void TextDiagnostic::emitSnippetAndCaret(
 
     if (!CaretLine.empty()) {
       indentForLineNumbers();
-      if (DiagOpts.ShowColors)
+      if (DiagOpts.showColors(OS.has_colors()))
         OS.changeColor(CaretColor, true);
       OS << CaretLine << '\n';
-      if (DiagOpts.ShowColors)
+      if (DiagOpts.showColors(OS.has_colors()))
         OS.resetColor();
     }
 
     if (!FixItInsertionLine.empty()) {
       indentForLineNumbers();
-      if (DiagOpts.ShowColors)
+      if (DiagOpts.showColors(OS.has_colors()))
         // Print fixit line in color
         OS.changeColor(FixitColor, false);
       if (DiagOpts.ShowSourceRanges)
         OS << ' ';
       OS << FixItInsertionLine << '\n';
-      if (DiagOpts.ShowColors)
+      if (DiagOpts.showColors(OS.has_colors()))
         OS.resetColor();
     }
   }
@@ -1552,7 +1553,7 @@ void TextDiagnostic::emitSnippet(StringRef SourceLine,
         printableTextForNextCharacter(SourceLine, &I, DiagOpts.TabStop);
 
     // Toggle inverted colors on or off for this character.
-    if (DiagOpts.ShowColors) {
+    if (DiagOpts.showColors(OS.has_colors())) {
       if (WasPrintable == PrintReversed) {
         PrintReversed = !PrintReversed;
         if (PrintReversed)
@@ -1583,7 +1584,7 @@ void TextDiagnostic::emitSnippet(StringRef SourceLine,
     OS << Str;
   }
 
-  if (DiagOpts.ShowColors)
+  if (DiagOpts.showColors(OS.has_colors()))
     OS.resetColor();
 
   OS << '\n';
diff --git a/clang/lib/Frontend/TextDiagnosticPrinter.cpp b/clang/lib/Frontend/TextDiagnosticPrinter.cpp
index 475f11e36977c..b016aa34cf0b5 100644
--- a/clang/lib/Frontend/TextDiagnosticPrinter.cpp
+++ b/clang/lib/Frontend/TextDiagnosticPrinter.cpp
@@ -134,11 +134,12 @@ void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
   // other infrastructure necessary when emitting more rich diagnostics.
   if (!Info.getLocation().isValid()) {
     if (DiagOpts.ShowLevel)
-      TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts.ShowColors);
+      TextDiagnostic::printDiagnosticLevel(
+          OS, Level, DiagOpts.showColors(OS.has_colors()));
     TextDiagnostic::printDiagnosticMessage(
         OS, /*IsSupplemental=*/Level == DiagnosticsEngine::Note,
         DiagMessageStream.str(), OS.tell() - StartOfLocationInfo,
-        DiagOpts.MessageLength, DiagOpts.ShowColors);
+        DiagOpts.MessageLength, DiagOpts.showColors(OS.has_colors()));
     OS.flush();
     return;
   }
diff --git a/clang/test/Misc/diagnostic-color-output-stream.cpp b/clang/test/Misc/diagnostic-color-output-stream.cpp
new file mode 100644
index 0000000000000..96da7747348c2
--- /dev/null
+++ b/clang/test/Misc/diagnostic-color-output-stream.cpp
@@ -0,0 +1,25 @@
+// REQUIRES: ansi-escape-sequences
+
+// Default ('auto'): a redirected (non-terminal) stderr must not get ANSI codes.
+// RUN: not %clang_cc1 -fsyntax-only -std=c++11 %s 2>&1 \
+// RUN:   | FileCheck --check-prefix=NOCOLOR %s
+
+// Forced off behaves the same.
+// RUN: not %clang_cc1 -fsyntax-only -std=c++11 -fno-color-diagnostics %s 2>&1 \
+// RUN:   | FileCheck --check-prefix=NOCOLOR %s
+
+// Forced on: ANSI codes are emitted even to a non-terminal.
+// RUN: not %clang_cc1 -fsyntax-only -std=c++11 -fcolor-diagnostics %s 2>&1 \
+// RUN:   | FileCheck --check-prefix=COLOR %s
+
+// A -diagnostic-log-file pointing at a regular file must not contain ANSI.
+// RUN: rm -f %t.log
+// RUN: not %clang_cc1 -fsyntax-only -std=c++11 -diagnostic-log-file %t.log %s 2>/dev/null
+// RUN: FileCheck --check-prefix=NOCOLOR %s < %t.log
+
+template <typename> struct foo {};
+void func(foo<int>);
+void g() { func(foo<double>()); }
+
+// COLOR: {{.\[0;1;36m}}double{{.\[0m}}
+// NOCOLOR-NOT: {{.\[0;1;36m}}
diff --git a/clang/unittests/Tooling/ToolingTest.cpp b/clang/unittests/Tooling/ToolingTest.cpp
index c3b8ffa00924e..0f6cfbd2266bb 100644
--- a/clang/unittests/Tooling/ToolingTest.cpp
+++ b/clang/unittests/Tooling/ToolingTest.cpp
@@ -651,11 +651,13 @@ struct CheckColoredDiagnosticsAction : public clang::ASTFrontendAction {
       : ShouldShowColor(ShouldShowColor) {}
   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
                                                  StringRef) override {
-    if (Compiler.getDiagnosticOpts().ShowColors != ShouldShowColor)
+    bool HasColors =
+        Compiler.getDiagnosticOpts().getShowColors() ==
+        (ShouldShowColor ? ShowColorsKind::On : ShowColorsKind::Off);
+    if (!HasColors)
       Compiler.getDiagnostics().Report(
           Compiler.getDiagnostics().getCustomDiagID(
-              DiagnosticsEngine::Fatal,
-              "getDiagnosticOpts().ShowColors != ShouldShowColor"));
+              DiagnosticsEngine::Fatal, "getShowColors() != expected"));
     return std::make_unique<ASTConsumer>();
   }
 
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index ab961416441fe..f815084424607 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -122,7 +122,8 @@ static unsigned getOptimizationLevel(llvm::opt::ArgList &args,
 
 bool Fortran::frontend::parseDiagnosticArgs(clang::DiagnosticOptions &opts,
                                             llvm::opt::ArgList &args) {
-  opts.ShowColors = parseShowColorsArgs(args);
+  opts.setShowColors(parseShowColorsArgs(args) ? clang::ShowColorsKind::On
+                                               : clang::ShowColorsKind::Off);
 
   return true;
 }
@@ -1097,8 +1098,10 @@ static bool parseDiagArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
 
   // Default to off for `flang -fc1`.
   bool showColors{parseShowColorsArgs(args, false)};
-  diags.getDiagnosticOptions().ShowColors = showColors;
-  res.getDiagnosticOpts().ShowColors = showColors;
+  auto colorsMode =
+      showColors ? clang::ShowColorsKind::On : clang::ShowColorsKind::Off;
+  diags.getDiagnosticOptions().setShowColors(colorsMode);
+  res.getDiagnosticOpts().setShowColors(colorsMode);
   res.getFrontendOpts().showColors = showColors;
   return !diags.hasUncompilableErrorOccurred();
 }
diff --git a/flang/lib/Frontend/TextDiagnosticPrinter.cpp b/flang/lib/Frontend/TextDiagnosticPrinter.cpp
index 911b78a109e2e..33d54d9ad0b9c 100644
--- a/flang/lib/Frontend/TextDiagnosticPrinter.cpp
+++ b/flang/lib/Frontend/TextDiagnosticPrinter.cpp
@@ -81,7 +81,7 @@ void TextDiagnosticPrinter::printLocForRemarks(
     llvm::sys::path::make_preferred(absPath);
 
     // Used for changing only the bold attribute
-    if (diagOpts.ShowColors)
+    if (diagOpts.showColors(os.has_colors()))
       os.changeColor(llvm::raw_ostream::SAVEDCOLOR, true);
 
     // Print path, file name, line and column
@@ -112,12 +112,12 @@ void TextDiagnosticPrinter::HandleDiagnostic(
   llvm::StringRef diagMsg;
   printLocForRemarks(diagMessageStream, diagMsg);
 
-  Fortran::frontend::TextDiagnostic::printDiagnosticLevel(os, level,
-                                                          diagOpts.ShowColors);
+  Fortran::frontend::TextDiagnostic::printDiagnosticLevel(
+      os, level, diagOpts.showColors(os.has_colors()));
   Fortran::frontend::TextDiagnostic::printDiagnosticMessage(
       os,
       /*IsSupplemental=*/level == clang::DiagnosticsEngine::Note, diagMsg,
-      diagOpts.ShowColors);
+      diagOpts.showColors(os.has_colors()));
 
   os.flush();
 }
diff --git a/flang/test/Driver/color-diagnostics.f90 b/flang/test/Driver/color-diagnostics.f90
index 7c471e39f923f..0fb4531fb4967 100644
--- a/flang/test/Driver/color-diagnostics.f90
+++ b/flang/test/Driver/color-diagnostics.f90
@@ -9,7 +9,7 @@
 ! RUN: not %flang_fc1 %s -fcolor-diagnostics 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
 ! RUN: not %flang_fc1 %s -fno-color-diagnostics 2>&1 \
-! RUN:     | FileCheck %s --check-prefix=UNSUPPORTED_COLOR_DIAGS
+! RUN:     | FileCheck %s --check-prefix=CHECK_NCD
 
 ! RUN: not %flang %s -fdiagnostics-color 2>&1 \
 ! RUN:     | FileCheck %s --check-prefix=CHECK_CD
@@ -31,7 +31,6 @@
 
 ! CHECK_NCD: Semantic errors in {{.*}}color-diagnostics.f90
 
-! UNSUPPORTED_COLOR_DIAGS: error: unknown argument: '-fno-color-diagnostics'
 ! UNSUPPORTED_DIAGS_COLOR: error: unknown argument: '-fdiagnostics-color'
 ! UNSUPPORTED_NO_DIAGS_COLOR: error: unknown argument: '-fno-diagnostics-color'
 
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 82fd9844cf96a..2bfa187c009a7 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -8477,14 +8477,19 @@ TypeSystemClang::dump(lldb::opaque_compiler_type_t type) const {
 namespace {
 struct ScopedASTColor {
   ScopedASTColor(clang::ASTContext &ast, bool show_colors)
-      : ast(ast), old_show_colors(ast.getDiagnostics().getShowColors()) {
-    ast.getDiagnostics().setShowColors(show_colors);
+      : ast(ast),
+        old_show_colors(
+            ast.getDiagnostics().getDiagnosticOptions().getShowColors()) {
+    ast.getDiagnostics().getDiagnosticOptions().setShowColors(
+        show_colors ? clang::ShowColorsKind::On : clang::ShowColorsKind::Off);
   }
 
-  ~ScopedASTColor() { ast.getDiagnostics().setShowColors(old_show_colors); }
+  ~ScopedASTColor() {
+    ast.getDiagnostics().getDiagnosticOptions().setShowColors(old_show_colors);
+  }
 
   clang::ASTContext *
-  const bool old_show_colors;
+  const clang::ShowColorsKind old_show_colors;
 };
 } // namespace
 



More information about the flang-commits mailing list