[flang-commits] [flang] 8d51d37 - [flang] Introduce DiagnosticConsumer classes in libflangFrontend

Andrzej Warzynski via flang-commits flang-commits at lists.llvm.org
Mon Oct 5 09:51:34 PDT 2020


Author: Andrzej Warzynski
Date: 2020-10-05T17:46:44+01:00
New Revision: 8d51d37e0628bde3eb5a3200507ba7135dfc2751

URL: https://github.com/llvm/llvm-project/commit/8d51d37e0628bde3eb5a3200507ba7135dfc2751
DIFF: https://github.com/llvm/llvm-project/commit/8d51d37e0628bde3eb5a3200507ba7135dfc2751.diff

LOG: [flang] Introduce DiagnosticConsumer classes in libflangFrontend

Currently Flang uses TextDiagnostic, TextDiagnosticPrinter &
TestDiagnosticBuffer classes from Clang (more specifically, from
libclangFrontend). This patch introduces simplified equivalents of these
classes in Flang (i.e. it removes the dependency on libclangFrontend).

Flang only needs these diagnostics classes for the compiler driver
diagnostics. This is unlike in Clang in which similar diagnostic classes
are used for e.g. Lexing/Parsing/Sema diagnostics. For this reason, the
implementations introduced here are relatively basic. We can extend them
in the future if this is required.

This patch also enhances how the diagnostics are printed. In particular,
this is the diagnostic that you'd get _before_  the changes introduced here
(no text formatting):

```
$ bin/flang-new
error: no input files
```

This is the diagnostic that you get _after_ the changes introduced here
(in terminals that support it, the text is formatted - bold + red):

```
$ bin/flang-new
flang-new: error: no input files
```

Tests are updated accordingly and options related to enabling/disabling
color diagnostics are flagged as supported by Flang.

Reviewed By: sameeranjoshi, CarolineConcatto

Differential Revision: https://reviews.llvm.org/D87774

Added: 
    flang/include/flang/Frontend/TextDiagnostic.h
    flang/include/flang/Frontend/TextDiagnosticBuffer.h
    flang/include/flang/Frontend/TextDiagnosticPrinter.h
    flang/lib/Frontend/TextDiagnostic.cpp
    flang/lib/Frontend/TextDiagnosticBuffer.cpp
    flang/lib/Frontend/TextDiagnosticPrinter.cpp

Modified: 
    clang/include/clang/Driver/Options.td
    flang/include/flang/Frontend/CompilerInvocation.h
    flang/lib/Frontend/CMakeLists.txt
    flang/lib/Frontend/CompilerInstance.cpp
    flang/lib/Frontend/CompilerInvocation.cpp
    flang/test/Flang-Driver/driver-error-cc1.c
    flang/test/Flang-Driver/driver-error-cc1.cpp
    flang/test/Flang-Driver/driver-help.f90
    flang/test/Flang-Driver/driver-version.f90
    flang/test/Flang-Driver/missing-input.f90
    flang/tools/flang-driver/driver.cpp
    flang/tools/flang-driver/fc1_main.cpp
    flang/unittests/Frontend/CompilerInstanceTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 18a123476253..e65a68c0deaa 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -876,7 +876,8 @@ def fclang_abi_compat_EQ : Joined<["-"], "fclang-abi-compat=">, Group<f_clang_Gr
   Flags<[CC1Option]>, MetaVarName<"<version>">, Values<"<major>.<minor>,latest">,
   HelpText<"Attempt to match the ABI of Clang <version>">;
 def fclasspath_EQ : Joined<["-"], "fclasspath=">, Group<f_Group>;
-defm color_diagnostics : OptInFFlag<"color-diagnostics", "Enable", "Disable", " colors in diagnostics", [CoreOption]>;
+defm color_diagnostics : OptInFFlag<"color-diagnostics", "Enable", "Disable", " colors in diagnostics",
+  [CoreOption, FlangOption]>;
 def fdiagnostics_color : Flag<["-"], "fdiagnostics-color">, Group<f_Group>,
   Flags<[CoreOption, DriverOption]>;
 def fdiagnostics_color_EQ : Joined<["-"], "fdiagnostics-color=">, Group<f_Group>;

diff  --git a/flang/include/flang/Frontend/CompilerInvocation.h b/flang/include/flang/Frontend/CompilerInvocation.h
index 0fa169fd1620..05f93293d0a5 100644
--- a/flang/include/flang/Frontend/CompilerInvocation.h
+++ b/flang/include/flang/Frontend/CompilerInvocation.h
@@ -11,8 +11,17 @@
 #include "flang/Frontend/FrontendOptions.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticOptions.h"
+#include "llvm/Option/ArgList.h"
 
 namespace Fortran::frontend {
+
+/// Fill out Opts based on the options given in Args.
+///
+/// When errors are encountered, return false and, if Diags is non-null,
+/// report the error(s).
+bool ParseDiagnosticArgs(clang::DiagnosticOptions &opts,
+    llvm::opt::ArgList &args, bool defaultDiagColor = true);
+
 class CompilerInvocationBase {
 public:
   /// Options controlling the diagnostic engine.$

diff  --git a/flang/include/flang/Frontend/TextDiagnostic.h b/flang/include/flang/Frontend/TextDiagnostic.h
new file mode 100644
index 000000000000..f803058c88c5
--- /dev/null
+++ b/flang/include/flang/Frontend/TextDiagnostic.h
@@ -0,0 +1,70 @@
+//===--- TextDiagnostic.h - Text Diagnostic Pretty-Printing -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A utility class that provides support for textual pretty-printing of
+// diagnostics. Based on clang::TextDiagnostic (this is a trimmed version).
+//
+// TODO: If expanding, consider sharing the implementation with Clang.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FLANG_FRONTEND_TEXTDIAGNOSTIC_H
+#define LLVM_FLANG_FRONTEND_TEXTDIAGNOSTIC_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+
+namespace Fortran::frontend {
+
+/// Class to encapsulate the logic for formatting and printing a textual
+/// diagnostic message.
+///
+/// The purpose of this class is to isolate the implementation of printing
+/// beautiful text diagnostics from any particular interfaces. Currently only
+/// simple diagnostics that lack source location information are supported (e.g.
+/// Flang driver errors).
+///
+/// In the future we can extend this class (akin to Clang) to support more
+/// complex diagnostics that would include macro backtraces, caret diagnostics,
+/// FixIt Hints and code snippets.
+///
+class TextDiagnostic {
+public:
+  TextDiagnostic();
+
+  ~TextDiagnostic();
+
+  /// Print the diagnostic level to a llvm::raw_ostream.
+  ///
+  /// This is a static helper that handles colorizing the level and formatting
+  /// it into an arbitrary output stream.
+  ///
+  /// \param os Where the message is printed
+  /// \param level The diagnostic level (e.g. error or warning)
+  /// \param showColors Enable colorizing of the message.
+  static void PrintDiagnosticLevel(llvm::raw_ostream &os,
+      clang::DiagnosticsEngine::Level level, bool showColors);
+
+  /// Pretty-print a diagnostic message to a llvm::raw_ostream.
+  ///
+  /// This is a static helper to handle the colorizing and rendering diagnostic
+  /// message to a particular ostream. In the future we can
+  /// extend it to support e.g. line wrapping. It is
+  /// publicly visible as at this stage we don't require any state data to
+  /// print a diagnostic.
+  ///
+  /// \param os Where the message is printed
+  /// \param isSupplemental true if this is a continuation note diagnostic
+  /// \param message The text actually printed
+  /// \param showColors Enable colorizing of the message.
+  static void PrintDiagnosticMessage(llvm::raw_ostream &os, bool isSupplemental,
+      llvm::StringRef message, bool showColors);
+};
+
+} // namespace Fortran::frontend
+
+#endif

diff  --git a/flang/include/flang/Frontend/TextDiagnosticBuffer.h b/flang/include/flang/Frontend/TextDiagnosticBuffer.h
new file mode 100644
index 000000000000..7322a5745c79
--- /dev/null
+++ b/flang/include/flang/Frontend/TextDiagnosticBuffer.h
@@ -0,0 +1,52 @@
+//===- TextDiagnosticBuffer.h - Buffer Text Diagnostics ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a concrete diagnostic client. The diagnostics are buffered rather
+// than printed. In order to print them, use the FlushDiagnostics method.
+// Pretty-printing is not supported.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FLANG_FRONTEND_TEXTDIAGNOSTICBUFFER_H
+#define LLVM_FLANG_FRONTEND_TEXTDIAGNOSTICBUFFER_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceLocation.h"
+#include <cstddef>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace Fortran::frontend {
+
+class TextDiagnosticBuffer : public clang::DiagnosticConsumer {
+public:
+  using DiagList = std::vector<std::pair<clang::SourceLocation, std::string>>;
+  using DiagnosticsLevelAndIndexPairs =
+      std::vector<std::pair<clang::DiagnosticsEngine::Level, size_t>>;
+
+private:
+  DiagList errors_, warnings_, remarks_, notes_;
+
+  /// All diagnostics in the order in which they were generated. That order
+  /// likely doesn't correspond to user input order, but at least it keeps
+  /// notes in the right places. Each pair is a diagnostic level and an index
+  /// into the corresponding DiagList above.
+  DiagnosticsLevelAndIndexPairs all_;
+
+public:
+  void HandleDiagnostic(clang::DiagnosticsEngine::Level diagLevel,
+      const clang::Diagnostic &info) override;
+
+  /// Flush the buffered diagnostics to a given diagnostic engine.
+  void FlushDiagnostics(clang::DiagnosticsEngine &diags) const;
+};
+
+} // namespace Fortran::frontend
+
+#endif // LLVM_CLANG_FRONTEND_TEXTDIAGNOSTICBUFFER_H

diff  --git a/flang/include/flang/Frontend/TextDiagnosticPrinter.h b/flang/include/flang/Frontend/TextDiagnosticPrinter.h
new file mode 100644
index 000000000000..b67731d84352
--- /dev/null
+++ b/flang/include/flang/Frontend/TextDiagnosticPrinter.h
@@ -0,0 +1,55 @@
+//===--- TextDiagnosticPrinter.h - Text Diagnostic Client -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a concrete diagnostic client. In terminals that support it, the
+// diagnostics are pretty-printed (colors + bold). The printing/flushing
+// happens in HandleDiagnostics (usually called at the point when the
+// diagnostic is generated).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FLANG_FRONTEND_TEXTDIAGNOSTICPRINTER_H
+#define LLVM_FLANG_FRONTEND_TEXTDIAGNOSTICPRINTER_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+class DiagnosticOptions;
+class DiagnosticsEngine;
+}; // namespace clang
+
+using llvm::IntrusiveRefCntPtr;
+using llvm::raw_ostream;
+
+namespace Fortran::frontend {
+class TextDiagnostic;
+
+class TextDiagnosticPrinter : public clang::DiagnosticConsumer {
+  raw_ostream &os_;
+  llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagOpts_;
+
+  /// A string to prefix to error messages.
+  std::string prefix_;
+
+public:
+  TextDiagnosticPrinter(raw_ostream &os, clang::DiagnosticOptions *diags);
+  ~TextDiagnosticPrinter() override;
+
+  /// Set the diagnostic printer prefix string, which will be printed at the
+  /// start of any diagnostics. If empty, no prefix string is used.
+  void set_prefix(std::string value) { prefix_ = std::move(value); }
+
+  void HandleDiagnostic(clang::DiagnosticsEngine::Level level,
+      const clang::Diagnostic &info) override;
+};
+
+} // namespace Fortran::frontend
+
+#endif

diff  --git a/flang/lib/Frontend/CMakeLists.txt b/flang/lib/Frontend/CMakeLists.txt
index fe74662c05ce..3cebc959c5ec 100644
--- a/flang/lib/Frontend/CMakeLists.txt
+++ b/flang/lib/Frontend/CMakeLists.txt
@@ -2,6 +2,9 @@ add_flang_library(flangFrontend
   CompilerInstance.cpp
   CompilerInvocation.cpp
   FrontendOptions.cpp
+  TextDiagnosticPrinter.cpp
+  TextDiagnosticBuffer.cpp
+  TextDiagnostic.cpp
 
   DEPENDS
   clangBasic
@@ -9,9 +12,6 @@ add_flang_library(flangFrontend
   LINK_LIBS
   clangBasic
   clangDriver
-  # TODO: Added to re-use clang's TextDiagnosticBuffer & TextDiagnosticPrinter.
-  # Add a custom implementation for Flang and remove this dependency.
-  clangFrontend
 
   LINK_COMPONENTS
   Option

diff  --git a/flang/lib/Frontend/CompilerInstance.cpp b/flang/lib/Frontend/CompilerInstance.cpp
index bf1461dd16ad..dd92639c3dc2 100644
--- a/flang/lib/Frontend/CompilerInstance.cpp
+++ b/flang/lib/Frontend/CompilerInstance.cpp
@@ -8,7 +8,7 @@
 
 #include "flang/Frontend/CompilerInstance.h"
 #include "flang/Frontend/CompilerInvocation.h"
-#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "flang/Frontend/TextDiagnosticPrinter.h"
 #include "llvm/Support/raw_ostream.h"
 
 using namespace Fortran::frontend;
@@ -36,7 +36,7 @@ CompilerInstance::CreateDiagnostics(clang::DiagnosticOptions *opts,
   if (client) {
     diags->setClient(client, shouldOwnClient);
   } else {
-    diags->setClient(new clang::TextDiagnosticPrinter(llvm::errs(), opts));
+    diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts));
   }
   return diags;
 }

diff  --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index c68ad5c11d65..aef5e3c95d05 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -17,6 +17,7 @@
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/OptTable.h"
+#include "llvm/Support/Process.h"
 #include "llvm/Support/raw_ostream.h"
 
 using namespace Fortran::frontend;
@@ -35,6 +36,48 @@ CompilerInvocationBase::~CompilerInvocationBase() = default;
 //===----------------------------------------------------------------------===//
 // Deserialization (from args)
 //===----------------------------------------------------------------------===//
+static bool parseShowColorsArgs(
+    const llvm::opt::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;
+
+  for (auto *a : args) {
+    const llvm::opt::Option &O = a->getOption();
+    if (O.matches(clang::driver::options::OPT_fcolor_diagnostics) ||
+        O.matches(clang::driver::options::OPT_fdiagnostics_color)) {
+      ShowColors = Colors_On;
+    } else if (O.matches(clang::driver::options::OPT_fno_color_diagnostics) ||
+        O.matches(clang::driver::options::OPT_fno_diagnostics_color)) {
+      ShowColors = Colors_Off;
+    } else if (O.matches(clang::driver::options::OPT_fdiagnostics_color_EQ)) {
+      llvm::StringRef value(a->getValue());
+      if (value == "always")
+        ShowColors = Colors_On;
+      else if (value == "never")
+        ShowColors = Colors_Off;
+      else if (value == "auto")
+        ShowColors = Colors_Auto;
+    }
+  }
+
+  return ShowColors == Colors_On ||
+      (ShowColors == Colors_Auto && llvm::sys::Process::StandardErrHasColors());
+}
+
+bool Fortran::frontend::ParseDiagnosticArgs(clang::DiagnosticOptions &opts,
+    llvm::opt::ArgList &args, bool defaultDiagColor) {
+  opts.ShowColors = parseShowColorsArgs(args, defaultDiagColor);
+
+  return true;
+}
+
 static InputKind ParseFrontendArgs(FrontendOptions &opts,
     llvm::opt::ArgList &args, clang::DiagnosticsEngine &diags) {
   // Identify the action (i.e. opts.ProgramAction)

diff  --git a/flang/lib/Frontend/TextDiagnostic.cpp b/flang/lib/Frontend/TextDiagnostic.cpp
new file mode 100644
index 000000000000..0a908537209e
--- /dev/null
+++ b/flang/lib/Frontend/TextDiagnostic.cpp
@@ -0,0 +1,97 @@
+//===--- TextDiagnostic.cpp - Text Diagnostic Pretty-Printing -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Frontend/TextDiagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace Fortran::frontend;
+
+// TODO: Similar enums are defined in clang/lib/Frontend/TextDiagnostic.cpp.
+// It would be best to share them
+static const enum llvm::raw_ostream::Colors noteColor =
+    llvm::raw_ostream::BLACK;
+static const enum llvm::raw_ostream::Colors remarkColor =
+    llvm::raw_ostream::BLUE;
+static const enum llvm::raw_ostream::Colors warningColor =
+    llvm::raw_ostream::MAGENTA;
+static const enum llvm::raw_ostream::Colors errorColor = llvm::raw_ostream::RED;
+static const enum llvm::raw_ostream::Colors fatalColor = llvm::raw_ostream::RED;
+// Used for changing only the bold attribute.
+static const enum llvm::raw_ostream::Colors savedColor =
+    llvm::raw_ostream::SAVEDCOLOR;
+
+TextDiagnostic::TextDiagnostic() {}
+
+TextDiagnostic::~TextDiagnostic() {}
+
+/*static*/ void TextDiagnostic::PrintDiagnosticLevel(llvm::raw_ostream &os,
+    clang::DiagnosticsEngine::Level level, bool showColors) {
+  if (showColors) {
+    // Print diagnostic category in bold and color
+    switch (level) {
+    case clang::DiagnosticsEngine::Ignored:
+      llvm_unreachable("Invalid diagnostic type");
+    case clang::DiagnosticsEngine::Note:
+      os.changeColor(noteColor, true);
+      break;
+    case clang::DiagnosticsEngine::Remark:
+      os.changeColor(remarkColor, true);
+      break;
+    case clang::DiagnosticsEngine::Warning:
+      os.changeColor(warningColor, true);
+      break;
+    case clang::DiagnosticsEngine::Error:
+      os.changeColor(errorColor, true);
+      break;
+    case clang::DiagnosticsEngine::Fatal:
+      os.changeColor(fatalColor, true);
+      break;
+    }
+  }
+
+  switch (level) {
+  case clang::DiagnosticsEngine::Ignored:
+    llvm_unreachable("Invalid diagnostic type");
+  case clang::DiagnosticsEngine::Note:
+    os << "note";
+    break;
+  case clang::DiagnosticsEngine::Remark:
+    os << "remark";
+    break;
+  case clang::DiagnosticsEngine::Warning:
+    os << "warning";
+    break;
+  case clang::DiagnosticsEngine::Error:
+    os << "error";
+    break;
+  case clang::DiagnosticsEngine::Fatal:
+    os << "fatal error";
+    break;
+  }
+
+  os << ": ";
+
+  if (showColors)
+    os.resetColor();
+}
+
+/*static*/
+void TextDiagnostic::PrintDiagnosticMessage(llvm::raw_ostream &os,
+    bool isSupplemental, llvm::StringRef message, bool showColors) {
+  if (showColors && !isSupplemental) {
+    // Print primary diagnostic messages in bold and without color.
+    os.changeColor(savedColor, true);
+  }
+
+  os << message;
+
+  if (showColors)
+    os.resetColor();
+  os << '\n';
+}

diff  --git a/flang/lib/Frontend/TextDiagnosticBuffer.cpp b/flang/lib/Frontend/TextDiagnosticBuffer.cpp
new file mode 100644
index 000000000000..6ac0f18b5ee3
--- /dev/null
+++ b/flang/lib/Frontend/TextDiagnosticBuffer.cpp
@@ -0,0 +1,74 @@
+//===- TextDiagnosticBuffer.cpp - Buffer Text Diagnostics -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a concrete diagnostic client, which buffers the diagnostic messages.
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Frontend/TextDiagnosticBuffer.h"
+#include "clang/Basic/Diagnostic.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace Fortran::frontend;
+
+/// HandleDiagnostic - Store the errors, warnings, and notes that are
+/// reported.
+void TextDiagnosticBuffer::HandleDiagnostic(
+    clang::DiagnosticsEngine::Level level, const clang::Diagnostic &info) {
+  // Default implementation (warnings_/errors count).
+  DiagnosticConsumer::HandleDiagnostic(level, info);
+
+  llvm::SmallString<100> buf;
+  info.FormatDiagnostic(buf);
+  switch (level) {
+  default:
+    llvm_unreachable("Diagnostic not handled during diagnostic buffering!");
+  case clang::DiagnosticsEngine::Note:
+    all_.emplace_back(level, notes_.size());
+    notes_.emplace_back(info.getLocation(), std::string(buf.str()));
+    break;
+  case clang::DiagnosticsEngine::Warning:
+    all_.emplace_back(level, warnings_.size());
+    warnings_.emplace_back(info.getLocation(), std::string(buf.str()));
+    break;
+  case clang::DiagnosticsEngine::Remark:
+    all_.emplace_back(level, remarks_.size());
+    remarks_.emplace_back(info.getLocation(), std::string(buf.str()));
+    break;
+  case clang::DiagnosticsEngine::Error:
+  case clang::DiagnosticsEngine::Fatal:
+    all_.emplace_back(level, errors_.size());
+    errors_.emplace_back(info.getLocation(), std::string(buf.str()));
+    break;
+  }
+}
+
+void TextDiagnosticBuffer::FlushDiagnostics(
+    clang::DiagnosticsEngine &Diags) const {
+  for (const auto &i : all_) {
+    auto Diag = Diags.Report(Diags.getCustomDiagID(i.first, "%0"));
+    switch (i.first) {
+    default:
+      llvm_unreachable("Diagnostic not handled during diagnostic flushing!");
+    case clang::DiagnosticsEngine::Note:
+      Diag << notes_[i.second].second;
+      break;
+    case clang::DiagnosticsEngine::Warning:
+      Diag << warnings_[i.second].second;
+      break;
+    case clang::DiagnosticsEngine::Remark:
+      Diag << remarks_[i.second].second;
+      break;
+    case clang::DiagnosticsEngine::Error:
+    case clang::DiagnosticsEngine::Fatal:
+      Diag << errors_[i.second].second;
+      break;
+    }
+  }
+}

diff  --git a/flang/lib/Frontend/TextDiagnosticPrinter.cpp b/flang/lib/Frontend/TextDiagnosticPrinter.cpp
new file mode 100644
index 000000000000..20cbe7540974
--- /dev/null
+++ b/flang/lib/Frontend/TextDiagnosticPrinter.cpp
@@ -0,0 +1,55 @@
+//===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This diagnostic client prints out their diagnostic messages.
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Frontend/TextDiagnosticPrinter.h"
+#include "flang/Frontend/TextDiagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace Fortran::frontend;
+
+TextDiagnosticPrinter::TextDiagnosticPrinter(
+    raw_ostream &os, clang::DiagnosticOptions *diags)
+    : os_(os), diagOpts_(diags) {}
+
+TextDiagnosticPrinter::~TextDiagnosticPrinter() {}
+
+void TextDiagnosticPrinter::HandleDiagnostic(
+    clang::DiagnosticsEngine::Level level, const clang::Diagnostic &info) {
+  // Default implementation (Warnings/errors count).
+  DiagnosticConsumer::HandleDiagnostic(level, info);
+
+  // Render the diagnostic message into a temporary buffer eagerly. We'll use
+  // this later as we print out the diagnostic to the terminal.
+  llvm::SmallString<100> outStr;
+  info.FormatDiagnostic(outStr);
+
+  llvm::raw_svector_ostream DiagMessageStream(outStr);
+
+  if (!prefix_.empty())
+    os_ << prefix_ << ": ";
+
+  // We only emit diagnostics in contexts that lack valid source locations.
+  assert(!info.getLocation().isValid() &&
+      "Diagnostics with valid source location are not supported");
+
+  Fortran::frontend::TextDiagnostic::PrintDiagnosticLevel(
+      os_, level, diagOpts_->ShowColors);
+  Fortran::frontend::TextDiagnostic::PrintDiagnosticMessage(os_,
+      /*IsSupplemental=*/level == clang::DiagnosticsEngine::Note,
+      DiagMessageStream.str(), diagOpts_->ShowColors);
+
+  os_.flush();
+  return;
+}

diff  --git a/flang/test/Flang-Driver/driver-error-cc1.c b/flang/test/Flang-Driver/driver-error-cc1.c
index 1563ee431579..aed0f74ecd0b 100644
--- a/flang/test/Flang-Driver/driver-error-cc1.c
+++ b/flang/test/Flang-Driver/driver-error-cc1.c
@@ -4,4 +4,4 @@
 
 // C files are currently not supported (i.e. `flang -cc1`)
 
-// CHECK:error: unknown integrated tool '-cc1'. Valid tools include '-fc1'.
+// CHECK: error: unknown integrated tool '-cc1'. Valid tools include '-fc1'.

diff  --git a/flang/test/Flang-Driver/driver-error-cc1.cpp b/flang/test/Flang-Driver/driver-error-cc1.cpp
index 20e469733bc9..0de39dc90ea2 100644
--- a/flang/test/Flang-Driver/driver-error-cc1.cpp
+++ b/flang/test/Flang-Driver/driver-error-cc1.cpp
@@ -4,4 +4,4 @@
 
 // C++ files are currently not supported (i.e. `flang -cc1`)
 
-// CHECK:error: unknown integrated tool '-cc1'. Valid tools include '-fc1'.
+// CHECK: error: unknown integrated tool '-cc1'. Valid tools include '-fc1'.

diff  --git a/flang/test/Flang-Driver/driver-help.f90 b/flang/test/Flang-Driver/driver-help.f90
index 6ecd076efee4..aafc5630b270 100644
--- a/flang/test/Flang-Driver/driver-help.f90
+++ b/flang/test/Flang-Driver/driver-help.f90
@@ -1,13 +1,21 @@
-! RUN: %flang-new -help 2>&1 | FileCheck %s
-! RUN: %flang-new -fc1 -help 2>&1 | FileCheck %s
+! RUN: %flang-new -help 2>&1 | FileCheck %s --check-prefix=HELP
+! RUN: %flang-new -fc1 -help 2>&1 | FileCheck %s --check-prefix=HELP-FC1
 ! RUN: not %flang-new -helps 2>&1 | FileCheck %s --check-prefix=ERROR
 
 ! REQUIRES: new-flang-driver
 
-! CHECK:USAGE: flang-new
-! CHECK-EMPTY:
-! CHECK-NEXT:OPTIONS:
-! CHECK-NEXT: -help     Display available options
-! CHECK-NEXT: --version Print version information
+! HELP:USAGE: flang-new
+! HELP-EMPTY:
+! HELP-NEXT:OPTIONS:
+! HELP-NEXT: -fcolor-diagnostics    Enable colors in diagnostics
+! HELP-NEXT: -fno-color-diagnostics Disable colors in diagnostics
+! HELP-NEXT: -help     Display available options
+! HELP-NEXT: --version Print version information
 
-! ERROR: error: unknown argument '-helps'; did you mean '-help'
+! HELP-FC1:USAGE: flang-new
+! HELP-FC1-EMPTY:
+! HELP-FC1-NEXT:OPTIONS:
+! HELP-FC1-NEXT: -help     Display available options
+! HELP-FC1-NEXT: --version Print version information
+
+! ERROR: flang-new: error: unknown argument '-helps'; did you mean '-help'

diff  --git a/flang/test/Flang-Driver/driver-version.f90 b/flang/test/Flang-Driver/driver-version.f90
index 8552d0b2f28b..199770bd9e50 100644
--- a/flang/test/Flang-Driver/driver-version.f90
+++ b/flang/test/Flang-Driver/driver-version.f90
@@ -8,4 +8,4 @@
 ! CHECK-NEXT:Thread model:
 ! CHECK-NEXT:InstalledDir:
 
-! ERROR: error: unsupported option '--versions'; did you mean '--version'?
+! ERROR: flang-new: error: unsupported option '--versions'; did you mean '--version'?

diff  --git a/flang/test/Flang-Driver/missing-input.f90 b/flang/test/Flang-Driver/missing-input.f90
index 96818bc4bd38..5e46395e8de6 100644
--- a/flang/test/Flang-Driver/missing-input.f90
+++ b/flang/test/Flang-Driver/missing-input.f90
@@ -2,4 +2,4 @@
 
 ! REQUIRES: new-flang-driver
 
-! CHECK: error: no input files
+! CHECK: flang-new: error: no input files

diff  --git a/flang/tools/flang-driver/driver.cpp b/flang/tools/flang-driver/driver.cpp
index 9d04994d9843..e00320096f38 100644
--- a/flang/tools/flang-driver/driver.cpp
+++ b/flang/tools/flang-driver/driver.cpp
@@ -11,11 +11,12 @@
 //
 //===----------------------------------------------------------------------===//
 #include "clang/Driver/Driver.h"
+#include "flang/Frontend/CompilerInvocation.h"
+#include "flang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticIDs.h"
 #include "clang/Basic/DiagnosticOptions.h"
 #include "clang/Driver/Compilation.h"
-#include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/Option/ArgList.h"
@@ -23,6 +24,8 @@
 #include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/VirtualFileSystem.h"
 
+using llvm::StringRef;
+
 // main frontend method. Lives inside fc1_main.cpp
 extern int fc1_main(llvm::ArrayRef<const char *> argv, const char *argv0);
 
@@ -37,6 +40,17 @@ std::string GetExecutablePath(const char *argv0) {
 static clang::DiagnosticOptions *CreateAndPopulateDiagOpts(
     llvm::ArrayRef<const char *> argv) {
   auto *diagOpts = new clang::DiagnosticOptions;
+
+  // Ignore missingArgCount and the return value of ParseDiagnosticArgs.
+  // Any errors that would be diagnosed here will also be diagnosed later,
+  // when the DiagnosticsEngine actually exists.
+  unsigned missingArgIndex, missingArgCount;
+  llvm::opt::InputArgList args = clang::driver::getDriverOptTable().ParseArgs(
+      argv.slice(1), missingArgIndex, missingArgCount,
+      /*FlagsToInclude=*/clang::driver::options::FlangOption);
+
+  (void)Fortran::frontend::ParseDiagnosticArgs(*diagOpts, args);
+
   return diagOpts;
 }
 
@@ -83,8 +97,12 @@ int main(int argc_, const char **argv_) {
       CreateAndPopulateDiagOpts(argv);
   llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID(
       new clang::DiagnosticIDs());
-  clang::TextDiagnosticPrinter *diagClient =
-      new clang::TextDiagnosticPrinter(llvm::errs(), &*diagOpts);
+  Fortran::frontend::TextDiagnosticPrinter *diagClient =
+      new Fortran::frontend::TextDiagnosticPrinter(llvm::errs(), &*diagOpts);
+
+  diagClient->set_prefix(
+      std::string(llvm::sys::path::stem(GetExecutablePath(argv[0]))));
+
   clang::DiagnosticsEngine diags(diagID, &*diagOpts, diagClient);
 
   // Prepare the driver

diff  --git a/flang/tools/flang-driver/fc1_main.cpp b/flang/tools/flang-driver/fc1_main.cpp
index bb69517edde2..5f7eeb1ea501 100644
--- a/flang/tools/flang-driver/fc1_main.cpp
+++ b/flang/tools/flang-driver/fc1_main.cpp
@@ -14,9 +14,9 @@
 
 #include "flang/Frontend/CompilerInstance.h"
 #include "flang/Frontend/CompilerInvocation.h"
+#include "flang/Frontend/TextDiagnosticBuffer.h"
 #include "flang/FrontendTool/Utils.h"
 #include "clang/Driver/DriverDiagnostic.h"
-#include "clang/Frontend/TextDiagnosticBuffer.h"
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/OptTable.h"
@@ -34,18 +34,22 @@ int fc1_main(llvm::ArrayRef<const char *> argv, const char *argv0) {
   if (!flang->HasDiagnostics())
     return 1;
 
+  // We will buffer diagnostics from argument parsing so that we can output
+  // them using a well formed diagnostic object.
+  TextDiagnosticBuffer *diagsBuffer = new TextDiagnosticBuffer;
+
   // Create CompilerInvocation - use a dedicated instance of DiagnosticsEngine
   // for parsing the arguments
   llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID(
       new clang::DiagnosticIDs());
   llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagOpts =
       new clang::DiagnosticOptions();
-  clang::TextDiagnosticBuffer *diagsBuffer = new clang::TextDiagnosticBuffer;
   clang::DiagnosticsEngine diags(diagID, &*diagOpts, diagsBuffer);
   bool success =
       CompilerInvocation::CreateFromArgs(flang->GetInvocation(), argv, diags);
 
   diagsBuffer->FlushDiagnostics(flang->getDiagnostics());
+
   if (!success)
     return 1;
 

diff  --git a/flang/unittests/Frontend/CompilerInstanceTest.cpp b/flang/unittests/Frontend/CompilerInstanceTest.cpp
index 866d097dc48b..04d581cd35cb 100644
--- a/flang/unittests/Frontend/CompilerInstanceTest.cpp
+++ b/flang/unittests/Frontend/CompilerInstanceTest.cpp
@@ -7,8 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "flang/Frontend/CompilerInstance.h"
+#include "flang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Basic/DiagnosticOptions.h"
-#include "clang/Frontend/TextDiagnosticPrinter.h"
 
 #include "gtest/gtest.h"
 
@@ -21,7 +21,7 @@ TEST(CompilerInstance, AllowDiagnosticLogWithUnownedDiagnosticConsumer) {
   // 1. Set-up a basic DiagnosticConsumer
   std::string diagnosticOutput;
   llvm::raw_string_ostream diagnosticsOS(diagnosticOutput);
-  auto diagPrinter = std::make_unique<clang::TextDiagnosticPrinter>(
+  auto diagPrinter = std::make_unique<Fortran::frontend::TextDiagnosticPrinter>(
       diagnosticsOS, new clang::DiagnosticOptions());
 
   // 2. Create a CompilerInstance (to manage a DiagnosticEngine)


        


More information about the flang-commits mailing list