Make Formatter a non-interface
I had originally created `Formatter` as an interface as I was intending to implement this differently for linux and windows (for terminal coloring). Color printing is instead implemented by the `Printer` interface / PIMPL classes. Replace the multi-boolean constructor with a `Style` struct, as this will make life easier when we want to add / remove flags. Bug: tint:282 Change-Id: I630073ed7a76c023348b66e8a8517b00b2b6a0d2 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31569 Commit-Queue: Ben Clayton <bclayton@google.com> Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
parent
28f7764704
commit
ecea5c8aec
|
@ -18,6 +18,7 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "src/diagnostic/diagnostic.h"
|
#include "src/diagnostic/diagnostic.h"
|
||||||
|
#include "src/diagnostic/printer.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace diag {
|
namespace diag {
|
||||||
|
@ -57,22 +58,26 @@ std::basic_ostream<CharT, Traits>& operator<<(
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BasicFormatter : public Formatter {
|
} // namespace
|
||||||
public:
|
|
||||||
struct State {
|
/// State holds the internal formatter state for a format() call.
|
||||||
|
struct Formatter::State {
|
||||||
|
/// Constructs a State associated with the given printer.
|
||||||
|
/// @param p the printer to write formatted messages to.
|
||||||
explicit State(Printer* p) : printer(p) {}
|
explicit State(Printer* p) : printer(p) {}
|
||||||
~State() { flush(); }
|
~State() { flush(); }
|
||||||
|
|
||||||
// set_style() sets the current style to new_style, flushing any pending
|
/// set_style() sets the current style to new_style, flushing any pending
|
||||||
// messages to the printer if the style changed.
|
/// messages to the printer if the style changed.
|
||||||
void set_style(const Style& new_style) {
|
/// @param new_style the new style to apply for future written messages.
|
||||||
|
void set_style(const diag::Style& new_style) {
|
||||||
if (style.color != new_style.color || style.bold != new_style.bold) {
|
if (style.color != new_style.color || style.bold != new_style.bold) {
|
||||||
flush();
|
flush();
|
||||||
style = new_style;
|
style = new_style;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// flush() writes any pending messages to the printer, clearing the buffer.
|
/// flush writes any pending messages to the printer, clearing the buffer.
|
||||||
void flush() {
|
void flush() {
|
||||||
auto str = stream.str();
|
auto str = stream.str();
|
||||||
if (str.length() > 0) {
|
if (str.length() > 0) {
|
||||||
|
@ -82,17 +87,21 @@ class BasicFormatter : public Formatter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// operator<<() queues msg to be written to the printer.
|
/// operator<< queues msg to be written to the printer.
|
||||||
|
/// @param msg the value or string to write to the printer
|
||||||
|
/// @returns this State so that calls can be chained
|
||||||
template <typename T>
|
template <typename T>
|
||||||
State& operator<<(const T& msg) {
|
State& operator<<(const T& msg) {
|
||||||
stream << msg;
|
stream << msg;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// newline() queues a newline to be written to the printer.
|
/// newline queues a newline to be written to the printer.
|
||||||
void newline() { stream << std::endl; }
|
void newline() { stream << std::endl; }
|
||||||
|
|
||||||
// repeat() queues the character c to be writen to the printer n times.
|
/// repeat queues the character c to be writen to the printer n times.
|
||||||
|
/// @param c the character to print |n| times
|
||||||
|
/// @param n the number of times to print character |c|
|
||||||
void repeat(char c, size_t n) {
|
void repeat(char c, size_t n) {
|
||||||
while (n-- > 0) {
|
while (n-- > 0) {
|
||||||
stream << c;
|
stream << c;
|
||||||
|
@ -101,16 +110,14 @@ class BasicFormatter : public Formatter {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Printer* printer;
|
Printer* printer;
|
||||||
Style style;
|
diag::Style style;
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
};
|
};
|
||||||
|
|
||||||
BasicFormatter(bool print_file, bool print_severity, bool print_line)
|
Formatter::Formatter() {}
|
||||||
: print_file_(print_file),
|
Formatter::Formatter(const Style& style) : style_(style) {}
|
||||||
print_severity_(print_severity),
|
|
||||||
print_line_(print_line) {}
|
|
||||||
|
|
||||||
void format(const List& list, Printer* printer) const override {
|
void Formatter::format(const List& list, Printer* printer) const {
|
||||||
State state{printer};
|
State state{printer};
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
@ -119,92 +126,84 @@ class BasicFormatter : public Formatter {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
state.newline();
|
state.newline();
|
||||||
}
|
}
|
||||||
format(diag, &state);
|
format(diag, state);
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
void Formatter::format(const Diagnostic& diag, State& state) const {
|
||||||
void format(const Diagnostic& diag, State* state) const {
|
|
||||||
auto const& src = diag.source;
|
auto const& src = diag.source;
|
||||||
auto const& rng = src.range;
|
auto const& rng = src.range;
|
||||||
|
|
||||||
state->set_style({Color::kDefault, true});
|
state.set_style({Color::kDefault, true});
|
||||||
|
|
||||||
if (print_file_ && src.file != nullptr && !src.file->path.empty()) {
|
if (style_.print_file && src.file != nullptr && !src.file->path.empty()) {
|
||||||
(*state) << src.file->path;
|
state << src.file->path;
|
||||||
if (rng.begin.line > 0) {
|
if (rng.begin.line > 0) {
|
||||||
(*state) << ":" << rng.begin;
|
state << ":" << rng.begin;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(*state) << rng.begin;
|
state << rng.begin;
|
||||||
}
|
}
|
||||||
if (print_severity_) {
|
if (style_.print_severity) {
|
||||||
switch (diag.severity) {
|
switch (diag.severity) {
|
||||||
case Severity::Warning:
|
case Severity::Warning:
|
||||||
state->set_style({Color::kYellow, true});
|
state.set_style({Color::kYellow, true});
|
||||||
break;
|
break;
|
||||||
case Severity::Error:
|
case Severity::Error:
|
||||||
case Severity::Fatal:
|
case Severity::Fatal:
|
||||||
state->set_style({Color::kRed, true});
|
state.set_style({Color::kRed, true});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
(*state) << " " << diag.severity;
|
state << " " << diag.severity;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->set_style({Color::kDefault, true});
|
state.set_style({Color::kDefault, true});
|
||||||
(*state) << ": " << diag.message;
|
state << ": " << diag.message;
|
||||||
|
|
||||||
if (print_line_ && src.file != nullptr && rng.begin.line > 0) {
|
if (style_.print_line && src.file != nullptr && rng.begin.line > 0) {
|
||||||
state->newline();
|
state.newline();
|
||||||
state->set_style({Color::kDefault, false});
|
state.set_style({Color::kDefault, false});
|
||||||
|
|
||||||
for (size_t line = rng.begin.line; line <= rng.end.line; line++) {
|
for (size_t line = rng.begin.line; line <= rng.end.line; line++) {
|
||||||
if (line < src.file->lines.size() + 1) {
|
if (line < src.file->lines.size() + 1) {
|
||||||
auto len = src.file->lines[line - 1].size();
|
auto len = src.file->lines[line - 1].size();
|
||||||
|
|
||||||
(*state) << src.file->lines[line - 1];
|
state << src.file->lines[line - 1];
|
||||||
state->newline();
|
|
||||||
state->set_style({Color::kCyan, false});
|
state.newline();
|
||||||
|
state.set_style({Color::kCyan, false});
|
||||||
|
|
||||||
if (line == rng.begin.line && line == rng.end.line) {
|
if (line == rng.begin.line && line == rng.end.line) {
|
||||||
// Single line
|
// Single line
|
||||||
state->repeat(' ', rng.begin.column - 1);
|
state.repeat(' ', rng.begin.column - 1);
|
||||||
state->repeat(
|
state.repeat('^',
|
||||||
'^', std::max<size_t>(rng.end.column - rng.begin.column, 1));
|
std::max<size_t>(rng.end.column - rng.begin.column, 1));
|
||||||
} else if (line == rng.begin.line) {
|
} else if (line == rng.begin.line) {
|
||||||
// Start of multi-line
|
// Start of multi-line
|
||||||
state->repeat(' ', rng.begin.column - 1);
|
state.repeat(' ', rng.begin.column - 1);
|
||||||
state->repeat('^', len - (rng.begin.column - 1));
|
state.repeat('^', len - (rng.begin.column - 1));
|
||||||
} else if (line == rng.end.line) {
|
} else if (line == rng.end.line) {
|
||||||
// End of multi-line
|
// End of multi-line
|
||||||
state->repeat('^', rng.end.column - 1);
|
state.repeat('^', rng.end.column - 1);
|
||||||
} else {
|
} else {
|
||||||
// Middle of multi-line
|
// Middle of multi-line
|
||||||
state->repeat('^', len);
|
state.repeat('^', len);
|
||||||
}
|
}
|
||||||
state->newline();
|
state.newline();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state->set_style({});
|
state.set_style({});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const bool print_file_ = false;
|
std::string Formatter::format(const List& list) const {
|
||||||
const bool print_severity_ = false;
|
StringPrinter printer;
|
||||||
const bool print_line_ = false;
|
format(list, &printer);
|
||||||
};
|
return printer.str();
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
std::unique_ptr<Formatter> Formatter::create(bool print_file,
|
|
||||||
bool print_severity,
|
|
||||||
bool print_line) {
|
|
||||||
return std::make_unique<BasicFormatter>(print_file, print_severity,
|
|
||||||
print_line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Formatter::~Formatter() = default;
|
Formatter::~Formatter() = default;
|
||||||
|
|
|
@ -18,39 +18,49 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "src/diagnostic/printer.h"
|
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace diag {
|
namespace diag {
|
||||||
|
|
||||||
|
class Diagnostic;
|
||||||
class List;
|
class List;
|
||||||
|
class Printer;
|
||||||
|
|
||||||
/// Formatters are used to format a list of diagnostics into a human readable
|
/// Formatter are used to print a list of diagnostics messages.
|
||||||
/// string.
|
|
||||||
class Formatter {
|
class Formatter {
|
||||||
public:
|
public:
|
||||||
/// @returns a basic diagnostic formatter
|
/// Style controls the formatter's output style.
|
||||||
/// @param print_file include the file path for each diagnostic
|
struct Style {
|
||||||
/// @param print_severity include the severity for each diagnostic
|
/// include the file path for each diagnostic
|
||||||
/// @param print_line include the source line(s) for the diagnostic
|
bool print_file = true;
|
||||||
static std::unique_ptr<Formatter> create(bool print_file,
|
/// include the severity for each diagnostic
|
||||||
bool print_severity,
|
bool print_severity = true;
|
||||||
bool print_line);
|
/// include the source line(s) for the diagnostic
|
||||||
|
bool print_line = true;
|
||||||
|
};
|
||||||
|
|
||||||
virtual ~Formatter();
|
/// Constructor for the formatter using a default style.
|
||||||
|
Formatter();
|
||||||
|
|
||||||
|
/// Constructor for the formatter using the custom style.
|
||||||
|
/// @param style the style used for the formatter.
|
||||||
|
explicit Formatter(const Style& style);
|
||||||
|
|
||||||
|
~Formatter();
|
||||||
|
|
||||||
/// format prints the formatted diagnostic list |list| to |printer|.
|
|
||||||
/// @param list the list of diagnostic messages to format
|
/// @param list the list of diagnostic messages to format
|
||||||
/// @param printer the printer used to display the formatted diagnostics
|
/// @param printer the printer used to display the formatted diagnostics
|
||||||
virtual void format(const List& list, Printer* printer) const = 0;
|
void format(const List& list, Printer* printer) const;
|
||||||
|
|
||||||
/// @return the list of diagnostics |list| formatted to a string.
|
/// @return the list of diagnostics |list| formatted to a string.
|
||||||
/// @param list the list of diagnostic messages to format
|
/// @param list the list of diagnostic messages to format
|
||||||
inline std::string format(const List& list) const {
|
std::string format(const List& list) const;
|
||||||
StringPrinter printer;
|
|
||||||
format(list, &printer);
|
private:
|
||||||
return printer.str();
|
struct State;
|
||||||
}
|
|
||||||
|
void format(const Diagnostic& diag, State& state) const;
|
||||||
|
|
||||||
|
Style const style_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace diag
|
} // namespace diag
|
||||||
|
|
|
@ -44,8 +44,8 @@ class DiagFormatterTest : public testing::Test {
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(DiagFormatterTest, Simple) {
|
TEST_F(DiagFormatterTest, Simple) {
|
||||||
auto fmt = Formatter::create(false, false, false);
|
Formatter fmt{{false, false, false}};
|
||||||
auto got = fmt->format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
auto got = fmt.format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
||||||
auto* expect = R"(1:14: purr
|
auto* expect = R"(1:14: purr
|
||||||
2:14: grrr
|
2:14: grrr
|
||||||
3:16: hiss
|
3:16: hiss
|
||||||
|
@ -54,8 +54,8 @@ TEST_F(DiagFormatterTest, Simple) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DiagFormatterTest, WithFile) {
|
TEST_F(DiagFormatterTest, WithFile) {
|
||||||
auto fmt = Formatter::create(true, false, false);
|
Formatter fmt{{true, false, false}};
|
||||||
auto got = fmt->format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
auto got = fmt.format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
||||||
auto* expect = R"(file.name:1:14: purr
|
auto* expect = R"(file.name:1:14: purr
|
||||||
file.name:2:14: grrr
|
file.name:2:14: grrr
|
||||||
file.name:3:16: hiss
|
file.name:3:16: hiss
|
||||||
|
@ -64,8 +64,8 @@ file.name:4:16: nothing)";
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DiagFormatterTest, WithSeverity) {
|
TEST_F(DiagFormatterTest, WithSeverity) {
|
||||||
auto fmt = Formatter::create(false, true, false);
|
Formatter fmt{{false, true, false}};
|
||||||
auto got = fmt->format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
auto got = fmt.format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
||||||
auto* expect = R"(1:14 info: purr
|
auto* expect = R"(1:14 info: purr
|
||||||
2:14 warning: grrr
|
2:14 warning: grrr
|
||||||
3:16 error: hiss
|
3:16 error: hiss
|
||||||
|
@ -74,8 +74,8 @@ TEST_F(DiagFormatterTest, WithSeverity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DiagFormatterTest, WithLine) {
|
TEST_F(DiagFormatterTest, WithLine) {
|
||||||
auto fmt = Formatter::create(false, false, true);
|
Formatter fmt{{false, false, true}};
|
||||||
auto got = fmt->format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
auto got = fmt.format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
||||||
auto* expect = R"(1:14: purr
|
auto* expect = R"(1:14: purr
|
||||||
the cat says meow
|
the cat says meow
|
||||||
^
|
^
|
||||||
|
@ -96,8 +96,8 @@ the snail says ???
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DiagFormatterTest, BasicWithFileSeverityLine) {
|
TEST_F(DiagFormatterTest, BasicWithFileSeverityLine) {
|
||||||
auto fmt = Formatter::create(true, true, true);
|
Formatter fmt{{true, true, true}};
|
||||||
auto got = fmt->format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
auto got = fmt.format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
||||||
auto* expect = R"(file.name:1:14 info: purr
|
auto* expect = R"(file.name:1:14 info: purr
|
||||||
the cat says meow
|
the cat says meow
|
||||||
^
|
^
|
||||||
|
@ -121,9 +121,8 @@ TEST_F(DiagFormatterTest, BasicWithMultiLine) {
|
||||||
Diagnostic multiline{Severity::Warning,
|
Diagnostic multiline{Severity::Warning,
|
||||||
Source{Source::Range{{2, 9}, {4, 15}}, &file},
|
Source{Source::Range{{2, 9}, {4, 15}}, &file},
|
||||||
"multiline"};
|
"multiline"};
|
||||||
|
Formatter fmt{{false, false, true}};
|
||||||
auto fmt = Formatter::create(false, false, true);
|
auto got = fmt.format(List{multiline});
|
||||||
auto got = fmt->format(List{multiline});
|
|
||||||
auto* expect = R"(2:9: multiline
|
auto* expect = R"(2:9: multiline
|
||||||
the dog says woof
|
the dog says woof
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
|
|
|
@ -104,7 +104,8 @@ class ParserImpl {
|
||||||
|
|
||||||
/// @returns the parser error string
|
/// @returns the parser error string
|
||||||
std::string error() const {
|
std::string error() const {
|
||||||
return diag::Formatter::create(false, false, false)->format(diags_);
|
diag::Formatter formatter{{false, false, false}};
|
||||||
|
return formatter.format(diags_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @returns the diagnostic messages
|
/// @returns the diagnostic messages
|
||||||
|
|
|
@ -30,9 +30,7 @@ class ParserImplErrorTest : public ParserImplTest {};
|
||||||
auto* p = parser(source); \
|
auto* p = parser(source); \
|
||||||
EXPECT_EQ(false, p->Parse()); \
|
EXPECT_EQ(false, p->Parse()); \
|
||||||
EXPECT_EQ(true, p->diagnostics().contains_errors()); \
|
EXPECT_EQ(true, p->diagnostics().contains_errors()); \
|
||||||
EXPECT_EQ( \
|
EXPECT_EQ(expected, diag::Formatter().format(p->diagnostics())); \
|
||||||
expected, \
|
|
||||||
diag::Formatter::create(true, true, true)->format(p->diagnostics())); \
|
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
TEST_F(ParserImplErrorTest, AdditiveInvalidExpr) {
|
TEST_F(ParserImplErrorTest, AdditiveInvalidExpr) {
|
||||||
|
|
Loading…
Reference in New Issue