Skip to content

Commit 8d51d37

Browse files
committed
[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
1 parent 1dce692 commit 8d51d37

19 files changed

+511
-25
lines changed

clang/include/clang/Driver/Options.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -876,7 +876,8 @@ def fclang_abi_compat_EQ : Joined<["-"], "fclang-abi-compat=">, Group<f_clang_Gr
876876
Flags<[CC1Option]>, MetaVarName<"<version>">, Values<"<major>.<minor>,latest">,
877877
HelpText<"Attempt to match the ABI of Clang <version>">;
878878
def fclasspath_EQ : Joined<["-"], "fclasspath=">, Group<f_Group>;
879-
defm color_diagnostics : OptInFFlag<"color-diagnostics", "Enable", "Disable", " colors in diagnostics", [CoreOption]>;
879+
defm color_diagnostics : OptInFFlag<"color-diagnostics", "Enable", "Disable", " colors in diagnostics",
880+
[CoreOption, FlangOption]>;
880881
def fdiagnostics_color : Flag<["-"], "fdiagnostics-color">, Group<f_Group>,
881882
Flags<[CoreOption, DriverOption]>;
882883
def fdiagnostics_color_EQ : Joined<["-"], "fdiagnostics-color=">, Group<f_Group>;

flang/include/flang/Frontend/CompilerInvocation.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,17 @@
1111
#include "flang/Frontend/FrontendOptions.h"
1212
#include "clang/Basic/Diagnostic.h"
1313
#include "clang/Basic/DiagnosticOptions.h"
14+
#include "llvm/Option/ArgList.h"
1415

1516
namespace Fortran::frontend {
17+
18+
/// Fill out Opts based on the options given in Args.
19+
///
20+
/// When errors are encountered, return false and, if Diags is non-null,
21+
/// report the error(s).
22+
bool ParseDiagnosticArgs(clang::DiagnosticOptions &opts,
23+
llvm::opt::ArgList &args, bool defaultDiagColor = true);
24+
1625
class CompilerInvocationBase {
1726
public:
1827
/// Options controlling the diagnostic engine.$
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===--- TextDiagnostic.h - Text Diagnostic Pretty-Printing -----*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// A utility class that provides support for textual pretty-printing of
10+
// diagnostics. Based on clang::TextDiagnostic (this is a trimmed version).
11+
//
12+
// TODO: If expanding, consider sharing the implementation with Clang.
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_FLANG_FRONTEND_TEXTDIAGNOSTIC_H
16+
#define LLVM_FLANG_FRONTEND_TEXTDIAGNOSTIC_H
17+
18+
#include "clang/Basic/Diagnostic.h"
19+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
20+
21+
namespace Fortran::frontend {
22+
23+
/// Class to encapsulate the logic for formatting and printing a textual
24+
/// diagnostic message.
25+
///
26+
/// The purpose of this class is to isolate the implementation of printing
27+
/// beautiful text diagnostics from any particular interfaces. Currently only
28+
/// simple diagnostics that lack source location information are supported (e.g.
29+
/// Flang driver errors).
30+
///
31+
/// In the future we can extend this class (akin to Clang) to support more
32+
/// complex diagnostics that would include macro backtraces, caret diagnostics,
33+
/// FixIt Hints and code snippets.
34+
///
35+
class TextDiagnostic {
36+
public:
37+
TextDiagnostic();
38+
39+
~TextDiagnostic();
40+
41+
/// Print the diagnostic level to a llvm::raw_ostream.
42+
///
43+
/// This is a static helper that handles colorizing the level and formatting
44+
/// it into an arbitrary output stream.
45+
///
46+
/// \param os Where the message is printed
47+
/// \param level The diagnostic level (e.g. error or warning)
48+
/// \param showColors Enable colorizing of the message.
49+
static void PrintDiagnosticLevel(llvm::raw_ostream &os,
50+
clang::DiagnosticsEngine::Level level, bool showColors);
51+
52+
/// Pretty-print a diagnostic message to a llvm::raw_ostream.
53+
///
54+
/// This is a static helper to handle the colorizing and rendering diagnostic
55+
/// message to a particular ostream. In the future we can
56+
/// extend it to support e.g. line wrapping. It is
57+
/// publicly visible as at this stage we don't require any state data to
58+
/// print a diagnostic.
59+
///
60+
/// \param os Where the message is printed
61+
/// \param isSupplemental true if this is a continuation note diagnostic
62+
/// \param message The text actually printed
63+
/// \param showColors Enable colorizing of the message.
64+
static void PrintDiagnosticMessage(llvm::raw_ostream &os, bool isSupplemental,
65+
llvm::StringRef message, bool showColors);
66+
};
67+
68+
} // namespace Fortran::frontend
69+
70+
#endif
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===- TextDiagnosticBuffer.h - Buffer Text Diagnostics ---------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This is a concrete diagnostic client. The diagnostics are buffered rather
10+
// than printed. In order to print them, use the FlushDiagnostics method.
11+
// Pretty-printing is not supported.
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_FLANG_FRONTEND_TEXTDIAGNOSTICBUFFER_H
16+
#define LLVM_FLANG_FRONTEND_TEXTDIAGNOSTICBUFFER_H
17+
18+
#include "clang/Basic/Diagnostic.h"
19+
#include "clang/Basic/SourceLocation.h"
20+
#include <cstddef>
21+
#include <string>
22+
#include <utility>
23+
#include <vector>
24+
25+
namespace Fortran::frontend {
26+
27+
class TextDiagnosticBuffer : public clang::DiagnosticConsumer {
28+
public:
29+
using DiagList = std::vector<std::pair<clang::SourceLocation, std::string>>;
30+
using DiagnosticsLevelAndIndexPairs =
31+
std::vector<std::pair<clang::DiagnosticsEngine::Level, size_t>>;
32+
33+
private:
34+
DiagList errors_, warnings_, remarks_, notes_;
35+
36+
/// All diagnostics in the order in which they were generated. That order
37+
/// likely doesn't correspond to user input order, but at least it keeps
38+
/// notes in the right places. Each pair is a diagnostic level and an index
39+
/// into the corresponding DiagList above.
40+
DiagnosticsLevelAndIndexPairs all_;
41+
42+
public:
43+
void HandleDiagnostic(clang::DiagnosticsEngine::Level diagLevel,
44+
const clang::Diagnostic &info) override;
45+
46+
/// Flush the buffered diagnostics to a given diagnostic engine.
47+
void FlushDiagnostics(clang::DiagnosticsEngine &diags) const;
48+
};
49+
50+
} // namespace Fortran::frontend
51+
52+
#endif // LLVM_CLANG_FRONTEND_TEXTDIAGNOSTICBUFFER_H
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//===--- TextDiagnosticPrinter.h - Text Diagnostic Client -------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This is a concrete diagnostic client. In terminals that support it, the
10+
// diagnostics are pretty-printed (colors + bold). The printing/flushing
11+
// happens in HandleDiagnostics (usually called at the point when the
12+
// diagnostic is generated).
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
#ifndef LLVM_FLANG_FRONTEND_TEXTDIAGNOSTICPRINTER_H
17+
#define LLVM_FLANG_FRONTEND_TEXTDIAGNOSTICPRINTER_H
18+
19+
#include "clang/Basic/Diagnostic.h"
20+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
21+
#include "llvm/Support/raw_ostream.h"
22+
23+
namespace clang {
24+
class DiagnosticOptions;
25+
class DiagnosticsEngine;
26+
}; // namespace clang
27+
28+
using llvm::IntrusiveRefCntPtr;
29+
using llvm::raw_ostream;
30+
31+
namespace Fortran::frontend {
32+
class TextDiagnostic;
33+
34+
class TextDiagnosticPrinter : public clang::DiagnosticConsumer {
35+
raw_ostream &os_;
36+
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagOpts_;
37+
38+
/// A string to prefix to error messages.
39+
std::string prefix_;
40+
41+
public:
42+
TextDiagnosticPrinter(raw_ostream &os, clang::DiagnosticOptions *diags);
43+
~TextDiagnosticPrinter() override;
44+
45+
/// Set the diagnostic printer prefix string, which will be printed at the
46+
/// start of any diagnostics. If empty, no prefix string is used.
47+
void set_prefix(std::string value) { prefix_ = std::move(value); }
48+
49+
void HandleDiagnostic(clang::DiagnosticsEngine::Level level,
50+
const clang::Diagnostic &info) override;
51+
};
52+
53+
} // namespace Fortran::frontend
54+
55+
#endif

flang/lib/Frontend/CMakeLists.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ add_flang_library(flangFrontend
22
CompilerInstance.cpp
33
CompilerInvocation.cpp
44
FrontendOptions.cpp
5+
TextDiagnosticPrinter.cpp
6+
TextDiagnosticBuffer.cpp
7+
TextDiagnostic.cpp
58

69
DEPENDS
710
clangBasic
811

912
LINK_LIBS
1013
clangBasic
1114
clangDriver
12-
# TODO: Added to re-use clang's TextDiagnosticBuffer & TextDiagnosticPrinter.
13-
# Add a custom implementation for Flang and remove this dependency.
14-
clangFrontend
1515

1616
LINK_COMPONENTS
1717
Option

flang/lib/Frontend/CompilerInstance.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
#include "flang/Frontend/CompilerInstance.h"
1010
#include "flang/Frontend/CompilerInvocation.h"
11-
#include "clang/Frontend/TextDiagnosticPrinter.h"
11+
#include "flang/Frontend/TextDiagnosticPrinter.h"
1212
#include "llvm/Support/raw_ostream.h"
1313

1414
using namespace Fortran::frontend;
@@ -36,7 +36,7 @@ CompilerInstance::CreateDiagnostics(clang::DiagnosticOptions *opts,
3636
if (client) {
3737
diags->setClient(client, shouldOwnClient);
3838
} else {
39-
diags->setClient(new clang::TextDiagnosticPrinter(llvm::errs(), opts));
39+
diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts));
4040
}
4141
return diags;
4242
}

flang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "llvm/Option/Arg.h"
1818
#include "llvm/Option/ArgList.h"
1919
#include "llvm/Option/OptTable.h"
20+
#include "llvm/Support/Process.h"
2021
#include "llvm/Support/raw_ostream.h"
2122

2223
using namespace Fortran::frontend;
@@ -35,6 +36,48 @@ CompilerInvocationBase::~CompilerInvocationBase() = default;
3536
//===----------------------------------------------------------------------===//
3637
// Deserialization (from args)
3738
//===----------------------------------------------------------------------===//
39+
static bool parseShowColorsArgs(
40+
const llvm::opt::ArgList &args, bool defaultColor) {
41+
// Color diagnostics default to auto ("on" if terminal supports) in the driver
42+
// but default to off in cc1, needing an explicit OPT_fdiagnostics_color.
43+
// Support both clang's -f[no-]color-diagnostics and gcc's
44+
// -f[no-]diagnostics-colors[=never|always|auto].
45+
enum {
46+
Colors_On,
47+
Colors_Off,
48+
Colors_Auto
49+
} ShowColors = defaultColor ? Colors_Auto : Colors_Off;
50+
51+
for (auto *a : args) {
52+
const llvm::opt::Option &O = a->getOption();
53+
if (O.matches(clang::driver::options::OPT_fcolor_diagnostics) ||
54+
O.matches(clang::driver::options::OPT_fdiagnostics_color)) {
55+
ShowColors = Colors_On;
56+
} else if (O.matches(clang::driver::options::OPT_fno_color_diagnostics) ||
57+
O.matches(clang::driver::options::OPT_fno_diagnostics_color)) {
58+
ShowColors = Colors_Off;
59+
} else if (O.matches(clang::driver::options::OPT_fdiagnostics_color_EQ)) {
60+
llvm::StringRef value(a->getValue());
61+
if (value == "always")
62+
ShowColors = Colors_On;
63+
else if (value == "never")
64+
ShowColors = Colors_Off;
65+
else if (value == "auto")
66+
ShowColors = Colors_Auto;
67+
}
68+
}
69+
70+
return ShowColors == Colors_On ||
71+
(ShowColors == Colors_Auto && llvm::sys::Process::StandardErrHasColors());
72+
}
73+
74+
bool Fortran::frontend::ParseDiagnosticArgs(clang::DiagnosticOptions &opts,
75+
llvm::opt::ArgList &args, bool defaultDiagColor) {
76+
opts.ShowColors = parseShowColorsArgs(args, defaultDiagColor);
77+
78+
return true;
79+
}
80+
3881
static InputKind ParseFrontendArgs(FrontendOptions &opts,
3982
llvm::opt::ArgList &args, clang::DiagnosticsEngine &diags) {
4083
// Identify the action (i.e. opts.ProgramAction)

0 commit comments

Comments
 (0)