From 3e66509f6cf4c6eb48b931e719ef6e2426758247 Mon Sep 17 00:00:00 2001 From: "Joel E. Denny" Date: Wed, 24 Oct 2018 21:46:42 +0000 Subject: [PATCH] [SourceMgr][FileCheck] Obey -color by extending WithColor (Relands r344930, reverted in r344935, and now hopefully fixed for Windows.) While this change specifically targets FileCheck, it affects any tool using the same SourceMgr facilities. Previously, -color was documented in FileCheck's -help output, but -color had no effect. Now, -color obeys its documentation: it forces colors to be used in FileCheck diagnostics even when stderr is not a terminal. -color is especially helpful when combined with FileCheck's -v, which can produce a long series of diagnostics that you might wish to pipe to a pager, such as less -R. The WithColor extensions here will also help to clean up color usage in FileCheck's annotated dump of input, which is proposed in D52999. Reviewed By: JDevlieghere, zturner Differential Revision: https://reviews.llvm.org/D53419 llvm-svn: 345202 --- llvm/docs/CommandGuide/FileCheck.rst | 4 ++ llvm/include/llvm/Support/WithColor.h | 63 +++++++++++++++++--- llvm/lib/Support/SourceMgr.cpp | 108 ++++++++++++++-------------------- llvm/lib/Support/WithColor.cpp | 63 +++++++++++++++----- llvm/test/FileCheck/opt-color.txt | 22 +++++++ llvm/utils/FileCheck/FileCheck.cpp | 5 ++ 6 files changed, 179 insertions(+), 86 deletions(-) create mode 100644 llvm/test/FileCheck/opt-color.txt diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst index 75df8a6..830b1e0 100644 --- a/llvm/docs/CommandGuide/FileCheck.rst +++ b/llvm/docs/CommandGuide/FileCheck.rst @@ -116,6 +116,10 @@ OPTIONS as old tests are migrated to the new non-overlapping ``CHECK-DAG:`` implementation. +.. option:: --color + + Use colors in output (autodetected by default). + EXIT STATUS ----------- diff --git a/llvm/include/llvm/Support/WithColor.h b/llvm/include/llvm/Support/WithColor.h index 85fc5fa..76842d1 100644 --- a/llvm/include/llvm/Support/WithColor.h +++ b/llvm/include/llvm/Support/WithColor.h @@ -29,23 +29,49 @@ enum class HighlightColor { Macro, Error, Warning, - Note + Note, + Remark }; /// An RAII object that temporarily switches an output stream to a specific /// color. class WithColor { raw_ostream &OS; - /// Determine whether colors should be displayed. - bool colorsEnabled(raw_ostream &OS); + bool DisableColors; public: /// To be used like this: WithColor(OS, HighlightColor::String) << "text"; - WithColor(raw_ostream &OS, HighlightColor S); + /// @param OS The output stream + /// @param S Symbolic name for syntax element to color + /// @param DisableColors Whether to ignore color changes regardless of -color + /// and support in OS + WithColor(raw_ostream &OS, HighlightColor S, bool DisableColors = false); + /// To be used like this: WithColor(OS, raw_ostream::Black) << "text"; + /// @param OS The output stream + /// @param Color ANSI color to use, the special SAVEDCOLOR can be used to + /// change only the bold attribute, and keep colors untouched + /// @param Bold Bold/brighter text, default false + /// @param BG If true, change the background, default: change foreground + /// @param DisableColors Whether to ignore color changes regardless of -color + /// and support in OS + WithColor(raw_ostream &OS, + raw_ostream::Colors Color = raw_ostream::SAVEDCOLOR, + bool Bold = false, bool BG = false, bool DisableColors = false) + : OS(OS), DisableColors(DisableColors) { + changeColor(Color, Bold, BG); + } ~WithColor(); raw_ostream &get() { return OS; } operator raw_ostream &() { return OS; } + template WithColor &operator<<(T &O) { + OS << O; + return *this; + } + template WithColor &operator<<(const T &O) { + OS << O; + return *this; + } /// Convenience method for printing "error: " to stderr. static raw_ostream &error(); @@ -53,13 +79,36 @@ public: static raw_ostream &warning(); /// Convenience method for printing "note: " to stderr. static raw_ostream ¬e(); + /// Convenience method for printing "remark: " to stderr. + static raw_ostream &remark(); /// Convenience method for printing "error: " to the given stream. - static raw_ostream &error(raw_ostream &OS, StringRef Prefix = ""); + static raw_ostream &error(raw_ostream &OS, StringRef Prefix = "", + bool DisableColors = false); /// Convenience method for printing "warning: " to the given stream. - static raw_ostream &warning(raw_ostream &OS, StringRef Prefix = ""); + static raw_ostream &warning(raw_ostream &OS, StringRef Prefix = "", + bool DisableColors = false); /// Convenience method for printing "note: " to the given stream. - static raw_ostream ¬e(raw_ostream &OS, StringRef Prefix = ""); + static raw_ostream ¬e(raw_ostream &OS, StringRef Prefix = "", + bool DisableColors = false); + /// Convenience method for printing "remark: " to the given stream. + static raw_ostream &remark(raw_ostream &OS, StringRef Prefix = "", + bool DisableColors = false); + + /// Determine whether colors are displayed. + bool colorsEnabled(); + + /// Change the color of text that will be output from this point forward. + /// @param Color ANSI color to use, the special SAVEDCOLOR can be used to + /// change only the bold attribute, and keep colors untouched + /// @param Bold Bold/brighter text, default false + /// @param BG If true, change the background, default: change foreground + WithColor &changeColor(raw_ostream::Colors Color, bool Bold = false, + bool BG = false); + + /// Reset the colors to terminal defaults. Call this when you are done + /// outputting colored text, or before program exit. + WithColor &resetColor(); }; } // end namespace llvm diff --git a/llvm/lib/Support/SourceMgr.cpp b/llvm/lib/Support/SourceMgr.cpp index 582e2cf..a55ad88 100644 --- a/llvm/lib/Support/SourceMgr.cpp +++ b/llvm/lib/Support/SourceMgr.cpp @@ -24,6 +24,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/SMLoc.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -370,65 +371,48 @@ static bool isNonASCII(char c) { return c & 0x80; } -void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors, - bool ShowKindLabel) const { - // Display colors only if OS supports colors. - ShowColors &= S.has_colors(); +void SMDiagnostic::print(const char *ProgName, raw_ostream &OS, + bool ShowColors, bool ShowKindLabel) const { + { + WithColor S(OS, raw_ostream::SAVEDCOLOR, true, false, !ShowColors); - if (ShowColors) - S.changeColor(raw_ostream::SAVEDCOLOR, true); + if (ProgName && ProgName[0]) + S << ProgName << ": "; - if (ProgName && ProgName[0]) - S << ProgName << ": "; + if (!Filename.empty()) { + if (Filename == "-") + S << ""; + else + S << Filename; - if (!Filename.empty()) { - if (Filename == "-") - S << ""; - else - S << Filename; - - if (LineNo != -1) { - S << ':' << LineNo; - if (ColumnNo != -1) - S << ':' << (ColumnNo+1); + if (LineNo != -1) { + S << ':' << LineNo; + if (ColumnNo != -1) + S << ':' << (ColumnNo + 1); + } + S << ": "; } - S << ": "; } if (ShowKindLabel) { switch (Kind) { case SourceMgr::DK_Error: - if (ShowColors) - S.changeColor(raw_ostream::RED, true); - S << "error: "; + WithColor::error(OS, "", !ShowColors); break; case SourceMgr::DK_Warning: - if (ShowColors) - S.changeColor(raw_ostream::MAGENTA, true); - S << "warning: "; + WithColor::warning(OS, "", !ShowColors); break; case SourceMgr::DK_Note: - if (ShowColors) - S.changeColor(raw_ostream::BLACK, true); - S << "note: "; + WithColor::note(OS, "", !ShowColors); break; case SourceMgr::DK_Remark: - if (ShowColors) - S.changeColor(raw_ostream::BLUE, true); - S << "remark: "; + WithColor::remark(OS, "", !ShowColors); break; } - - if (ShowColors) { - S.resetColor(); - S.changeColor(raw_ostream::SAVEDCOLOR, true); - } } - S << Message << '\n'; - - if (ShowColors) - S.resetColor(); + WithColor(OS, raw_ostream::SAVEDCOLOR, true, false, !ShowColors) + << Message << '\n'; if (LineNo == -1 || ColumnNo == -1) return; @@ -439,7 +423,7 @@ void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors, // expanding them later, and bail out rather than show incorrect ranges and // misaligned fixits for any other odd characters. if (find_if(LineContents, isNonASCII) != LineContents.end()) { - printSourceLine(S, LineContents); + printSourceLine(OS, LineContents); return; } size_t NumColumns = LineContents.size(); @@ -473,29 +457,27 @@ void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors, // least. CaretLine.erase(CaretLine.find_last_not_of(' ')+1); - printSourceLine(S, LineContents); + printSourceLine(OS, LineContents); - if (ShowColors) - S.changeColor(raw_ostream::GREEN, true); + { + WithColor S(OS, raw_ostream::GREEN, true, false, !ShowColors); - // Print out the caret line, matching tabs in the source line. - for (unsigned i = 0, e = CaretLine.size(), OutCol = 0; i != e; ++i) { - if (i >= LineContents.size() || LineContents[i] != '\t') { - S << CaretLine[i]; - ++OutCol; - continue; - } + // Print out the caret line, matching tabs in the source line. + for (unsigned i = 0, e = CaretLine.size(), OutCol = 0; i != e; ++i) { + if (i >= LineContents.size() || LineContents[i] != '\t') { + S << CaretLine[i]; + ++OutCol; + continue; + } - // Okay, we have a tab. Insert the appropriate number of characters. - do { - S << CaretLine[i]; - ++OutCol; - } while ((OutCol % TabStop) != 0); + // Okay, we have a tab. Insert the appropriate number of characters. + do { + S << CaretLine[i]; + ++OutCol; + } while ((OutCol % TabStop) != 0); + } + S << '\n'; } - S << '\n'; - - if (ShowColors) - S.resetColor(); // Print out the replacement line, matching tabs in the source line. if (FixItInsertionLine.empty()) @@ -503,14 +485,14 @@ void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors, for (size_t i = 0, e = FixItInsertionLine.size(), OutCol = 0; i < e; ++i) { if (i >= LineContents.size() || LineContents[i] != '\t') { - S << FixItInsertionLine[i]; + OS << FixItInsertionLine[i]; ++OutCol; continue; } // Okay, we have a tab. Insert the appropriate number of characters. do { - S << FixItInsertionLine[i]; + OS << FixItInsertionLine[i]; // FIXME: This is trying not to break up replacements, but then to re-sync // with the tabs between replacements. This will fail, though, if two // fix-it replacements are exactly adjacent, or if a fix-it contains a @@ -521,5 +503,5 @@ void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors, ++OutCol; } while (((OutCol % TabStop) != 0) && i != e); } - S << '\n'; + OS << '\n'; } diff --git a/llvm/lib/Support/WithColor.cpp b/llvm/lib/Support/WithColor.cpp index d2e13f0..cf4c109 100644 --- a/llvm/lib/Support/WithColor.cpp +++ b/llvm/lib/Support/WithColor.cpp @@ -19,15 +19,10 @@ static cl::opt cl::desc("Use colors in output (default=autodetect)"), cl::init(cl::BOU_UNSET)); -bool WithColor::colorsEnabled(raw_ostream &OS) { - if (UseColor == cl::BOU_UNSET) - return OS.has_colors(); - return UseColor == cl::BOU_TRUE; -} - -WithColor::WithColor(raw_ostream &OS, HighlightColor Color) : OS(OS) { +WithColor::WithColor(raw_ostream &OS, HighlightColor Color, bool DisableColors) + : OS(OS), DisableColors(DisableColors) { // Detect color from terminal type unless the user passed the --color option. - if (colorsEnabled(OS)) { + if (colorsEnabled()) { switch (Color) { case HighlightColor::Address: OS.changeColor(raw_ostream::YELLOW); @@ -56,6 +51,9 @@ WithColor::WithColor(raw_ostream &OS, HighlightColor Color) : OS(OS) { case HighlightColor::Note: OS.changeColor(raw_ostream::BLACK, true); break; + case HighlightColor::Remark: + OS.changeColor(raw_ostream::BLUE, true); + break; } } } @@ -66,25 +64,58 @@ raw_ostream &WithColor::warning() { return warning(errs()); } raw_ostream &WithColor::note() { return note(errs()); } -raw_ostream &WithColor::error(raw_ostream &OS, StringRef Prefix) { +raw_ostream &WithColor::remark() { return remark(errs()); } + +raw_ostream &WithColor::error(raw_ostream &OS, StringRef Prefix, + bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Error).get() << "error: "; + return WithColor(OS, HighlightColor::Error, DisableColors).get() + << "error: "; } -raw_ostream &WithColor::warning(raw_ostream &OS, StringRef Prefix) { +raw_ostream &WithColor::warning(raw_ostream &OS, StringRef Prefix, + bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Warning).get() << "warning: "; + return WithColor(OS, HighlightColor::Warning, DisableColors).get() + << "warning: "; } -raw_ostream &WithColor::note(raw_ostream &OS, StringRef Prefix) { +raw_ostream &WithColor::note(raw_ostream &OS, StringRef Prefix, + bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Note).get() << "note: "; + return WithColor(OS, HighlightColor::Note, DisableColors).get() << "note: "; } -WithColor::~WithColor() { - if (colorsEnabled(OS)) +raw_ostream &WithColor::remark(raw_ostream &OS, StringRef Prefix, + bool DisableColors) { + if (!Prefix.empty()) + OS << Prefix << ": "; + return WithColor(OS, HighlightColor::Remark, DisableColors).get() + << "remark: "; +} + +bool WithColor::colorsEnabled() { + if (DisableColors) + return false; + if (UseColor == cl::BOU_UNSET) + return OS.has_colors(); + return UseColor == cl::BOU_TRUE; +} + +WithColor &WithColor::changeColor(raw_ostream::Colors Color, bool Bold, + bool BG) { + if (colorsEnabled()) + OS.changeColor(Color, Bold, BG); + return *this; +} + +WithColor &WithColor::resetColor() { + if (colorsEnabled()) OS.resetColor(); + return *this; } + +WithColor::~WithColor() { resetColor(); } diff --git a/llvm/test/FileCheck/opt-color.txt b/llvm/test/FileCheck/opt-color.txt new file mode 100644 index 0000000..9430114 --- /dev/null +++ b/llvm/test/FileCheck/opt-color.txt @@ -0,0 +1,22 @@ +; Create a case that produces a simple diagnostic. +; RUN: echo foo > %t.in +; CHECK: bar + +; Run without and with -color. In the former case, FileCheck should suppress +; color in its diagnostics because stderr is a file. +; RUN: not FileCheck %s < %t.in 2> %t.no-color +; RUN: not FileCheck -color %s < %t.in 2> %t.color + +; Check whether color was produced. +; RUN: FileCheck -check-prefix NO-COLOR %s < %t.no-color +; RUN: FileCheck -check-prefix COLOR %s < %t.color + +; Make sure our NO-COLOR and COLOR patterns are sane: they don't match the +; opposite cases. +; RUN: not FileCheck -check-prefix COLOR %s < %t.no-color +; RUN: not FileCheck -check-prefix NO-COLOR %s < %t.color + +; I don't know of a good way to check for ANSI color codes, so just make sure +; some new characters show up where those codes should appear. +; NO-COLOR: : error: CHECK: expected string not found in input +; COLOR: : {{.+}}error: {{.+}}CHECK: expected string not found in input diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp index 8ce1d9c..bf3c3983 100644 --- a/llvm/utils/FileCheck/FileCheck.cpp +++ b/llvm/utils/FileCheck/FileCheck.cpp @@ -18,6 +18,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/FileCheck.h" using namespace llvm; @@ -108,6 +109,10 @@ static void DumpCommandLine(int argc, char **argv) { } int main(int argc, char **argv) { + // Enable use of ANSI color codes because FileCheck is using them to + // highlight text. + llvm::sys::Process::UseANSIEscapeCodes(true); + InitLLVM X(argc, argv); cl::ParseCommandLineOptions(argc, argv); -- 2.7.4