llvm-project/flang/lib/Frontend/CompilerInvocation.cpp
Andrzej Warzynski 8d51d37e06 [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
2020-10-05 17:46:44 +01:00

159 lines
5.8 KiB
C++

//===- CompilerInvocation.cpp ---------------------------------------------===//
//
// 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/CompilerInvocation.h"
#include "clang/Basic/AllDiagnostics.h"
#include "clang/Basic/DiagnosticDriver.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/Options.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#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;
//===----------------------------------------------------------------------===//
// Initialization.
//===----------------------------------------------------------------------===//
CompilerInvocationBase::CompilerInvocationBase()
: diagnosticOpts_(new clang::DiagnosticOptions()) {}
CompilerInvocationBase::CompilerInvocationBase(const CompilerInvocationBase &x)
: diagnosticOpts_(new clang::DiagnosticOptions(x.GetDiagnosticOpts())) {}
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)
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_Action_Group)) {
switch (a->getOption().getID()) {
default: {
llvm_unreachable("Invalid option in group!");
}
// TODO:
// case clang::driver::options::OPT_E:
// case clang::driver::options::OPT_emit_obj:
// case calng::driver::options::OPT_emit_llvm:
// case clang::driver::options::OPT_emit_llvm_only:
// case clang::driver::options::OPT_emit_codegen_only:
// case clang::driver::options::OPT_emit_module:
// (...)
}
}
opts.showHelp_ = args.hasArg(clang::driver::options::OPT_help);
opts.showVersion_ = args.hasArg(clang::driver::options::OPT_version);
// Get the input kind (from the value passed via `-x`)
InputKind dashX(Language::Unknown);
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_x)) {
llvm::StringRef XValue = a->getValue();
// Principal languages.
dashX = llvm::StringSwitch<InputKind>(XValue)
.Case("f90", Language::Fortran)
.Default(Language::Unknown);
// Some special cases cannot be combined with suffixes.
if (dashX.IsUnknown())
dashX = llvm::StringSwitch<InputKind>(XValue)
.Case("ir", Language::LLVM_IR)
.Default(Language::Unknown);
if (dashX.IsUnknown())
diags.Report(clang::diag::err_drv_invalid_value)
<< a->getAsString(args) << a->getValue();
}
return dashX;
}
bool CompilerInvocation::CreateFromArgs(CompilerInvocation &res,
llvm::ArrayRef<const char *> commandLineArgs,
clang::DiagnosticsEngine &diags) {
bool success = true;
// Parse the arguments
const llvm::opt::OptTable &opts = clang::driver::getDriverOptTable();
const unsigned includedFlagsBitmask =
clang::driver::options::FC1Option;
unsigned missingArgIndex, missingArgCount;
llvm::opt::InputArgList args = opts.ParseArgs(
commandLineArgs, missingArgIndex, missingArgCount, includedFlagsBitmask);
// Issue errors on unknown arguments
for (const auto *a : args.filtered(clang::driver::options::OPT_UNKNOWN)) {
auto argString = a->getAsString(args);
std::string nearest;
if (opts.findNearest(argString, nearest, includedFlagsBitmask) > 1)
diags.Report(clang::diag::err_drv_unknown_argument) << argString;
else
diags.Report(clang::diag::err_drv_unknown_argument_with_suggestion)
<< argString << nearest;
success = false;
}
// Parse the frontend args
ParseFrontendArgs(res.GetFrontendOpts(), args, diags);
return success;
}