tint->dawn: Shuffle source tree in preperation of merging repos

docs/    -> docs/tint/
fuzzers/ -> src/tint/fuzzers/
samples/ -> src/tint/cmd/
src/     -> src/tint/
test/    -> test/tint/

BUG=tint:1418,tint:1433

Change-Id: Id2aa79f989aef3245b80ef4aa37a27ff16cd700b
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/80482
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
This commit is contained in:
Ryan Harrison
2022-02-21 15:19:07 +00:00
committed by Tint LUCI CQ
parent 38f1e9c75c
commit dbc13af287
12231 changed files with 4897 additions and 4871 deletions

View File

@@ -0,0 +1,48 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/diagnostic/diagnostic.h"
#include <unordered_map>
#include "src/tint/diagnostic/formatter.h"
namespace tint {
namespace diag {
Diagnostic::Diagnostic() = default;
Diagnostic::Diagnostic(const Diagnostic&) = default;
Diagnostic::~Diagnostic() = default;
Diagnostic& Diagnostic::operator=(const Diagnostic&) = default;
List::List() = default;
List::List(std::initializer_list<Diagnostic> list) : entries_(list) {}
List::List(const List& rhs) = default;
List::List(List&& rhs) = default;
List::~List() = default;
List& List::operator=(const List& rhs) = default;
List& List::operator=(List&& rhs) = default;
std::string List::str() const {
diag::Formatter::Style style;
style.print_newline_at_end = false;
return Formatter{style}.format(*this);
}
} // namespace diag
} // namespace tint

View File

@@ -0,0 +1,252 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TINT_DIAGNOSTIC_DIAGNOSTIC_H_
#define SRC_TINT_DIAGNOSTIC_DIAGNOSTIC_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "src/tint/source.h"
namespace tint {
namespace diag {
/// Severity is an enumerator of diagnostic severities.
enum class Severity { Note, Warning, Error, InternalCompilerError, Fatal };
/// @return true iff `a` is more than, or of equal severity to `b`
inline bool operator>=(Severity a, Severity b) {
return static_cast<int>(a) >= static_cast<int>(b);
}
/// System is an enumerator of Tint systems that can be the originator of a
/// diagnostic message.
enum class System {
AST,
Clone,
Inspector,
Program,
ProgramBuilder,
Reader,
Resolver,
Semantic,
Symbol,
Test,
Transform,
Utils,
Writer,
};
/// Diagnostic holds all the information for a single compiler diagnostic
/// message.
class Diagnostic {
public:
/// Constructor
Diagnostic();
/// Copy constructor
Diagnostic(const Diagnostic&);
/// Destructor
~Diagnostic();
/// Copy assignment operator
/// @return this diagnostic
Diagnostic& operator=(const Diagnostic&);
/// severity is the severity of the diagnostic message.
Severity severity = Severity::Error;
/// source is the location of the diagnostic.
Source source;
/// message is the text associated with the diagnostic.
std::string message;
/// system is the Tint system that raised the diagnostic.
System system;
/// code is the error code, for example a validation error might have the code
/// `"v-0001"`.
const char* code = nullptr;
/// A shared pointer to a Source::File. Only used if the diagnostic Source
/// points to a file that was created specifically for this diagnostic
/// (usually an ICE).
std::shared_ptr<Source::File> owned_file = nullptr;
};
/// List is a container of Diagnostic messages.
class List {
public:
/// iterator is the type used for range based iteration.
using iterator = std::vector<Diagnostic>::const_iterator;
/// Constructs the list with no elements.
List();
/// Copy constructor. Copies the diagnostics from `list` into this list.
/// @param list the list of diagnostics to copy into this list.
List(std::initializer_list<Diagnostic> list);
/// Copy constructor. Copies the diagnostics from `list` into this list.
/// @param list the list of diagnostics to copy into this list.
List(const List& list);
/// Move constructor. Moves the diagnostics from `list` into this list.
/// @param list the list of diagnostics to move into this list.
List(List&& list);
/// Destructor
~List();
/// Assignment operator. Copies the diagnostics from `list` into this list.
/// @param list the list to copy into this list.
/// @return this list.
List& operator=(const List& list);
/// Assignment move operator. Moves the diagnostics from `list` into this
/// list.
/// @param list the list to move into this list.
/// @return this list.
List& operator=(List&& list);
/// adds a diagnostic to the end of this list.
/// @param diag the diagnostic to append to this list.
void add(Diagnostic&& diag) {
if (diag.severity >= Severity::Error) {
error_count_++;
}
entries_.emplace_back(std::move(diag));
}
/// adds a list of diagnostics to the end of this list.
/// @param list the diagnostic to append to this list.
void add(const List& list) {
for (auto diag : list) {
add(std::move(diag));
}
}
/// adds the note message with the given Source to the end of this list.
/// @param system the system raising the note message
/// @param note_msg the note message
/// @param source the source of the note diagnostic
void add_note(System system,
const std::string& note_msg,
const Source& source) {
diag::Diagnostic note{};
note.severity = diag::Severity::Note;
note.system = system;
note.source = source;
note.message = note_msg;
add(std::move(note));
}
/// adds the warning message with the given Source to the end of this list.
/// @param system the system raising the warning message
/// @param warning_msg the warning message
/// @param source the source of the warning diagnostic
void add_warning(System system,
const std::string& warning_msg,
const Source& source) {
diag::Diagnostic warning{};
warning.severity = diag::Severity::Warning;
warning.system = system;
warning.source = source;
warning.message = warning_msg;
add(std::move(warning));
}
/// adds the error message without a source to the end of this list.
/// @param system the system raising the error message
/// @param err_msg the error message
void add_error(System system, std::string err_msg) {
diag::Diagnostic error{};
error.severity = diag::Severity::Error;
error.system = system;
error.message = std::move(err_msg);
add(std::move(error));
}
/// adds the error message with the given Source to the end of this list.
/// @param system the system raising the error message
/// @param err_msg the error message
/// @param source the source of the error diagnostic
void add_error(System system, std::string err_msg, const Source& source) {
diag::Diagnostic error{};
error.severity = diag::Severity::Error;
error.system = system;
error.source = source;
error.message = std::move(err_msg);
add(std::move(error));
}
/// adds the error message with the given code and Source to the end of this
/// list.
/// @param system the system raising the error message
/// @param code the error code
/// @param err_msg the error message
/// @param source the source of the error diagnostic
void add_error(System system,
const char* code,
std::string err_msg,
const Source& source) {
diag::Diagnostic error{};
error.code = code;
error.severity = diag::Severity::Error;
error.system = system;
error.source = source;
error.message = std::move(err_msg);
add(std::move(error));
}
/// adds an internal compiler error message to the end of this list.
/// @param system the system raising the error message
/// @param err_msg the error message
/// @param source the source of the internal compiler error
/// @param file the Source::File owned by this diagnostic
void add_ice(System system,
const std::string& err_msg,
const Source& source,
std::shared_ptr<Source::File> file) {
diag::Diagnostic ice{};
ice.severity = diag::Severity::InternalCompilerError;
ice.system = system;
ice.source = source;
ice.message = err_msg;
ice.owned_file = std::move(file);
add(std::move(ice));
}
/// @returns true iff the diagnostic list contains errors diagnostics (or of
/// higher severity).
bool contains_errors() const { return error_count_ > 0; }
/// @returns the number of error diagnostics (or of higher severity).
size_t error_count() const { return error_count_; }
/// @returns the number of entries in the list.
size_t count() const { return entries_.size(); }
/// @returns the first diagnostic in the list.
iterator begin() const { return entries_.begin(); }
/// @returns the last diagnostic in the list.
iterator end() const { return entries_.end(); }
/// @returns a formatted string of all the diagnostics in this list.
std::string str() const;
private:
std::vector<Diagnostic> entries_;
size_t error_count_ = 0;
};
} // namespace diag
} // namespace tint
#endif // SRC_TINT_DIAGNOSTIC_DIAGNOSTIC_H_

View File

@@ -0,0 +1,42 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/diagnostic/formatter.h"
#include "gtest/gtest.h"
#include "src/tint/diagnostic/diagnostic.h"
namespace tint {
namespace diag {
namespace {
TEST(DiagListTest, OwnedFilesShared) {
auto file = std::make_shared<Source::File>("path", "content");
diag::List list_a, list_b;
{
diag::Diagnostic diag{};
diag.source = Source{Source::Range{{0, 0}}, file.get()};
list_a.add(std::move(diag));
}
list_b = list_a;
ASSERT_EQ(list_b.count(), list_a.count());
EXPECT_EQ(list_b.begin()->source.file, file.get());
}
} // namespace
} // namespace diag
} // namespace tint

View File

@@ -0,0 +1,271 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/diagnostic/formatter.h"
#include <algorithm>
#include <iterator>
#include <vector>
#include "src/tint/diagnostic/diagnostic.h"
#include "src/tint/diagnostic/printer.h"
namespace tint {
namespace diag {
namespace {
const char* to_str(Severity severity) {
switch (severity) {
case Severity::Note:
return "note";
case Severity::Warning:
return "warning";
case Severity::Error:
return "error";
case Severity::InternalCompilerError:
return "internal compiler error";
case Severity::Fatal:
return "fatal";
}
return "";
}
std::string to_str(const Source::Location& location) {
std::stringstream ss;
if (location.line > 0) {
ss << location.line;
if (location.column > 0) {
ss << ":" << location.column;
}
}
return ss.str();
}
} // namespace
/// 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) {}
~State() { flush(); }
/// set_style() sets the current style to new_style, flushing any pending
/// messages to the printer if the style changed.
/// @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) {
flush();
style = new_style;
}
}
/// flush writes any pending messages to the printer, clearing the buffer.
void flush() {
auto str = stream.str();
if (str.length() > 0) {
printer->write(str, style);
std::stringstream reset;
stream.swap(reset);
}
}
/// 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>
State& operator<<(const T& msg) {
stream << msg;
return *this;
}
/// newline queues a newline to be written to the printer.
void newline() { stream << std::endl; }
/// repeat queues the character c to be written 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) {
std::fill_n(std::ostream_iterator<char>(stream), n, c);
}
private:
Printer* printer;
diag::Style style;
std::stringstream stream;
};
Formatter::Formatter() {}
Formatter::Formatter(const Style& style) : style_(style) {}
void Formatter::format(const List& list, Printer* printer) const {
State state{printer};
bool first = true;
for (auto diag : list) {
state.set_style({});
if (!first) {
state.newline();
}
format(diag, state);
first = false;
}
if (style_.print_newline_at_end) {
state.newline();
}
}
void Formatter::format(const Diagnostic& diag, State& state) const {
auto const& src = diag.source;
auto const& rng = src.range;
bool has_code = diag.code != nullptr && diag.code[0] != '\0';
state.set_style({Color::kDefault, true});
struct TextAndColor {
std::string text;
Color color;
bool bold = false;
};
std::vector<TextAndColor> prefix;
prefix.reserve(6);
if (style_.print_file && src.file != nullptr) {
if (rng.begin.line > 0) {
prefix.emplace_back(TextAndColor{src.file->path + ":" + to_str(rng.begin),
Color::kDefault});
} else {
prefix.emplace_back(TextAndColor{src.file->path, Color::kDefault});
}
} else if (rng.begin.line > 0) {
prefix.emplace_back(TextAndColor{to_str(rng.begin), Color::kDefault});
}
Color severity_color = Color::kDefault;
switch (diag.severity) {
case Severity::Note:
break;
case Severity::Warning:
severity_color = Color::kYellow;
break;
case Severity::Error:
severity_color = Color::kRed;
break;
case Severity::Fatal:
case Severity::InternalCompilerError:
severity_color = Color::kMagenta;
break;
}
if (style_.print_severity) {
prefix.emplace_back(
TextAndColor{to_str(diag.severity), severity_color, true});
}
if (has_code) {
prefix.emplace_back(TextAndColor{diag.code, severity_color});
}
for (size_t i = 0; i < prefix.size(); i++) {
if (i > 0) {
state << " ";
}
state.set_style({prefix[i].color, prefix[i].bold});
state << prefix[i].text;
}
state.set_style({Color::kDefault, true});
if (!prefix.empty()) {
state << ": ";
}
state << diag.message;
if (style_.print_line && src.file && rng.begin.line > 0) {
state.newline();
state.set_style({Color::kDefault, false});
for (size_t line_num = rng.begin.line;
(line_num <= rng.end.line) &&
(line_num <= src.file->content.lines.size());
line_num++) {
auto& line = src.file->content.lines[line_num - 1];
auto line_len = line.size();
bool is_ascii = true;
for (auto c : line) {
if (c == '\t') {
state.repeat(' ', style_.tab_width);
} else {
state << c;
}
if (c & 0x80) {
is_ascii = false;
}
}
state.newline();
// If the line contains non-ascii characters, then we cannot assume that
// a single utf8 code unit represents a single glyph, so don't attempt to
// draw squiggles.
if (!is_ascii) {
continue;
}
state.set_style({Color::kCyan, false});
// Count the number of glyphs in the line span.
// start and end use 1-based indexing.
auto num_glyphs = [&](size_t start, size_t end) {
size_t count = 0;
start = (start > 0) ? (start - 1) : 0;
end = (end > 0) ? (end - 1) : 0;
for (size_t i = start; (i < end) && (i < line_len); i++) {
count += (line[i] == '\t') ? style_.tab_width : 1;
}
return count;
};
if (line_num == rng.begin.line && line_num == rng.end.line) {
// Single line
state.repeat(' ', num_glyphs(1, rng.begin.column));
state.repeat('^', std::max<size_t>(
num_glyphs(rng.begin.column, rng.end.column), 1));
} else if (line_num == rng.begin.line) {
// Start of multi-line
state.repeat(' ', num_glyphs(1, rng.begin.column));
state.repeat('^', num_glyphs(rng.begin.column, line_len + 1));
} else if (line_num == rng.end.line) {
// End of multi-line
state.repeat('^', num_glyphs(1, rng.end.column));
} else {
// Middle of multi-line
state.repeat('^', num_glyphs(1, line_len + 1));
}
state.newline();
}
state.set_style({});
}
}
std::string Formatter::format(const List& list) const {
StringPrinter printer;
format(list, &printer);
return printer.str();
}
Formatter::~Formatter() = default;
} // namespace diag
} // namespace tint

View File

@@ -0,0 +1,72 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TINT_DIAGNOSTIC_FORMATTER_H_
#define SRC_TINT_DIAGNOSTIC_FORMATTER_H_
#include <string>
namespace tint {
namespace diag {
class Diagnostic;
class List;
class Printer;
/// Formatter are used to print a list of diagnostics messages.
class Formatter {
public:
/// Style controls the formatter's output style.
struct Style {
/// include the file path for each diagnostic
bool print_file = true;
/// include the severity for each diagnostic
bool print_severity = true;
/// include the source line(s) for the diagnostic
bool print_line = true;
/// print a newline at the end of a diagnostic list
bool print_newline_at_end = true;
/// width of a tab character
size_t tab_width = 2u;
};
/// 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();
/// @param list the list of diagnostic messages to format
/// @param printer the printer used to display the formatted diagnostics
void format(const List& list, Printer* printer) const;
/// @return the list of diagnostics `list` formatted to a string.
/// @param list the list of diagnostic messages to format
std::string format(const List& list) const;
private:
struct State;
void format(const Diagnostic& diag, State& state) const;
const Style style_;
};
} // namespace diag
} // namespace tint
#endif // SRC_TINT_DIAGNOSTIC_FORMATTER_H_

View File

@@ -0,0 +1,310 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/diagnostic/formatter.h"
#include <utility>
#include "gtest/gtest.h"
#include "src/tint/diagnostic/diagnostic.h"
namespace tint {
namespace diag {
namespace {
Diagnostic Diag(Severity severity,
Source source,
std::string message,
System system,
const char* code = nullptr) {
Diagnostic d;
d.severity = severity;
d.source = source;
d.message = std::move(message);
d.system = system;
d.code = code;
return d;
}
constexpr const char* ascii_content = // Note: words are tab-delimited
R"(the cat says meow
the dog says woof
the snake says quack
the snail says ???
)";
constexpr const char* utf8_content = // Note: words are tab-delimited
"the \xf0\x9f\x90\xb1 says meow\n" // NOLINT: tabs
"the \xf0\x9f\x90\x95 says woof\n" // NOLINT: tabs
"the \xf0\x9f\x90\x8d says quack\n" // NOLINT: tabs
"the \xf0\x9f\x90\x8c says ???\n"; // NOLINT: tabs
class DiagFormatterTest : public testing::Test {
public:
Source::File ascii_file{"file.name", ascii_content};
Source::File utf8_file{"file.name", utf8_content};
Diagnostic ascii_diag_note =
Diag(Severity::Note,
Source{Source::Range{Source::Location{1, 14}}, &ascii_file},
"purr",
System::Test);
Diagnostic ascii_diag_warn =
Diag(Severity::Warning,
Source{Source::Range{{2, 14}, {2, 18}}, &ascii_file},
"grrr",
System::Test);
Diagnostic ascii_diag_err =
Diag(Severity::Error,
Source{Source::Range{{3, 16}, {3, 21}}, &ascii_file},
"hiss",
System::Test,
"abc123");
Diagnostic ascii_diag_ice =
Diag(Severity::InternalCompilerError,
Source{Source::Range{{4, 16}, {4, 19}}, &ascii_file},
"unreachable",
System::Test);
Diagnostic ascii_diag_fatal =
Diag(Severity::Fatal,
Source{Source::Range{{4, 16}, {4, 19}}, &ascii_file},
"nothing",
System::Test);
Diagnostic utf8_diag_note =
Diag(Severity::Note,
Source{Source::Range{Source::Location{1, 15}}, &utf8_file},
"purr",
System::Test);
Diagnostic utf8_diag_warn =
Diag(Severity::Warning,
Source{Source::Range{{2, 15}, {2, 19}}, &utf8_file},
"grrr",
System::Test);
Diagnostic utf8_diag_err =
Diag(Severity::Error,
Source{Source::Range{{3, 15}, {3, 20}}, &utf8_file},
"hiss",
System::Test,
"abc123");
Diagnostic utf8_diag_ice =
Diag(Severity::InternalCompilerError,
Source{Source::Range{{4, 15}, {4, 18}}, &utf8_file},
"unreachable",
System::Test);
Diagnostic utf8_diag_fatal =
Diag(Severity::Fatal,
Source{Source::Range{{4, 15}, {4, 18}}, &utf8_file},
"nothing",
System::Test);
};
TEST_F(DiagFormatterTest, Simple) {
Formatter fmt{{false, false, false, false}};
auto got = fmt.format(List{ascii_diag_note, ascii_diag_warn, ascii_diag_err});
auto* expect = R"(1:14: purr
2:14: grrr
3:16 abc123: hiss)";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, SimpleNewlineAtEnd) {
Formatter fmt{{false, false, false, true}};
auto got = fmt.format(List{ascii_diag_note, ascii_diag_warn, ascii_diag_err});
auto* expect = R"(1:14: purr
2:14: grrr
3:16 abc123: hiss
)";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, SimpleNoSource) {
Formatter fmt{{false, false, false, false}};
auto diag = Diag(Severity::Note, Source{}, "no source!", System::Test);
auto got = fmt.format(List{diag});
auto* expect = "no source!";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, WithFile) {
Formatter fmt{{true, false, false, false}};
auto got = fmt.format(List{ascii_diag_note, ascii_diag_warn, ascii_diag_err});
auto* expect = R"(file.name:1:14: purr
file.name:2:14: grrr
file.name:3:16 abc123: hiss)";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, WithSeverity) {
Formatter fmt{{false, true, false, false}};
auto got = fmt.format(List{ascii_diag_note, ascii_diag_warn, ascii_diag_err});
auto* expect = R"(1:14 note: purr
2:14 warning: grrr
3:16 error abc123: hiss)";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, WithLine) {
Formatter fmt{{false, false, true, false}};
auto got = fmt.format(List{ascii_diag_note, ascii_diag_warn, ascii_diag_err});
auto* expect = R"(1:14: purr
the cat says meow
^
2:14: grrr
the dog says woof
^^^^
3:16 abc123: hiss
the snake says quack
^^^^^
)";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, UnicodeWithLine) {
Formatter fmt{{false, false, true, false}};
auto got = fmt.format(List{utf8_diag_note, utf8_diag_warn, utf8_diag_err});
auto* expect =
"1:15: purr\n"
"the \xf0\x9f\x90\xb1 says meow\n"
"\n"
"2:15: grrr\n"
"the \xf0\x9f\x90\x95 says woof\n"
"\n"
"3:15 abc123: hiss\n"
"the \xf0\x9f\x90\x8d says quack\n";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, BasicWithFileSeverityLine) {
Formatter fmt{{true, true, true, false}};
auto got = fmt.format(List{ascii_diag_note, ascii_diag_warn, ascii_diag_err});
auto* expect = R"(file.name:1:14 note: purr
the cat says meow
^
file.name:2:14 warning: grrr
the dog says woof
^^^^
file.name:3:16 error abc123: hiss
the snake says quack
^^^^^
)";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, BasicWithMultiLine) {
auto multiline = Diag(Severity::Warning,
Source{Source::Range{{2, 9}, {4, 15}}, &ascii_file},
"multiline", System::Test);
Formatter fmt{{false, false, true, false}};
auto got = fmt.format(List{multiline});
auto* expect = R"(2:9: multiline
the dog says woof
^^^^^^^^^^
the snake says quack
^^^^^^^^^^^^^^^^^^^^^^^
the snail says ???
^^^^^^^^^^^^^^^^
)";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, UnicodeWithMultiLine) {
auto multiline = Diag(Severity::Warning,
Source{Source::Range{{2, 9}, {4, 15}}, &utf8_file},
"multiline", System::Test);
Formatter fmt{{false, false, true, false}};
auto got = fmt.format(List{multiline});
auto* expect =
"2:9: multiline\n"
"the \xf0\x9f\x90\x95 says woof\n"
"the \xf0\x9f\x90\x8d says quack\n"
"the \xf0\x9f\x90\x8c says ???\n";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, BasicWithFileSeverityLineTab4) {
Formatter fmt{{true, true, true, false, 4u}};
auto got = fmt.format(List{ascii_diag_note, ascii_diag_warn, ascii_diag_err});
auto* expect = R"(file.name:1:14 note: purr
the cat says meow
^
file.name:2:14 warning: grrr
the dog says woof
^^^^
file.name:3:16 error abc123: hiss
the snake says quack
^^^^^
)";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, BasicWithMultiLineTab4) {
auto multiline = Diag(Severity::Warning,
Source{Source::Range{{2, 9}, {4, 15}}, &ascii_file},
"multiline", System::Test);
Formatter fmt{{false, false, true, false, 4u}};
auto got = fmt.format(List{multiline});
auto* expect = R"(2:9: multiline
the dog says woof
^^^^^^^^^^^^
the snake says quack
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
the snail says ???
^^^^^^^^^^^^^^^^^^^^
)";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, ICE) {
Formatter fmt{{}};
auto got = fmt.format(List{ascii_diag_ice});
auto* expect = R"(file.name:4:16 internal compiler error: unreachable
the snail says ???
^^^
)";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, Fatal) {
Formatter fmt{{}};
auto got = fmt.format(List{ascii_diag_fatal});
auto* expect = R"(file.name:4:16 fatal: nothing
the snail says ???
^^^
)";
ASSERT_EQ(expect, got);
}
TEST_F(DiagFormatterTest, RangeOOB) {
Formatter fmt{{true, true, true, true}};
diag::List list;
list.add_error(System::Test, "oob",
Source{{{10, 20}, {30, 20}}, &ascii_file});
auto got = fmt.format(list);
auto* expect = R"(file.name:10:20 error: oob
)";
ASSERT_EQ(expect, got);
}
} // namespace
} // namespace diag
} // namespace tint

View File

@@ -0,0 +1,34 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/diagnostic/printer.h"
namespace tint {
namespace diag {
Printer::~Printer() = default;
StringPrinter::StringPrinter() = default;
StringPrinter::~StringPrinter() = default;
std::string StringPrinter::str() const {
return stream.str();
}
void StringPrinter::write(const std::string& str, const Style&) {
stream << str;
}
} // namespace diag
} // namespace tint

View File

@@ -0,0 +1,83 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TINT_DIAGNOSTIC_PRINTER_H_
#define SRC_TINT_DIAGNOSTIC_PRINTER_H_
#include <memory>
#include <sstream>
#include <string>
namespace tint {
namespace diag {
class List;
/// Color is an enumerator of colors used by Style.
enum class Color {
kDefault,
kBlack,
kRed,
kGreen,
kYellow,
kBlue,
kMagenta,
kCyan,
kWhite,
};
/// Style describes how a diagnostic message should be printed.
struct Style {
/// The foreground text color
Color color = Color::kDefault;
/// If true the text will be displayed with a strong weight
bool bold = false;
};
/// Printers are used to print formatted diagnostic messages to a terminal.
class Printer {
public:
/// @returns a diagnostic Printer
/// @param out the file to print to.
/// @param use_colors if true, the printer will use colors if `out` is a
/// terminal and supports them.
static std::unique_ptr<Printer> create(FILE* out, bool use_colors);
virtual ~Printer();
/// writes the string str to the printer with the given style.
/// @param str the string to write to the printer
/// @param style the style used to print `str`
virtual void write(const std::string& str, const Style& style) = 0;
};
/// StringPrinter is an implementation of Printer that writes to a std::string.
class StringPrinter : public Printer {
public:
StringPrinter();
~StringPrinter() override;
/// @returns the printed string.
std::string str() const;
void write(const std::string& str, const Style&) override;
private:
std::stringstream stream;
};
} // namespace diag
} // namespace tint
#endif // SRC_TINT_DIAGNOSTIC_PRINTER_H_

View File

@@ -0,0 +1,100 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <unistd.h>
#include <cstring>
#include "src/tint/diagnostic/printer.h"
namespace tint {
namespace diag {
namespace {
bool supports_colors(FILE* f) {
if (!isatty(fileno(f))) {
return false;
}
const char* cterm = getenv("TERM");
if (cterm == nullptr) {
return false;
}
std::string term = getenv("TERM");
if (term != "cygwin" && term != "linux" && term != "rxvt-unicode-256color" &&
term != "rxvt-unicode" && term != "screen-256color" && term != "screen" &&
term != "tmux-256color" && term != "tmux" && term != "xterm-256color" &&
term != "xterm-color" && term != "xterm") {
return false;
}
return true;
}
class PrinterLinux : public Printer {
public:
PrinterLinux(FILE* f, bool colors)
: file(f), use_colors(colors && supports_colors(f)) {}
void write(const std::string& str, const Style& style) override {
write_color(style.color, style.bold);
fwrite(str.data(), 1, str.size(), file);
write_color(Color::kDefault, false);
}
private:
constexpr const char* color_code(Color color, bool bold) {
switch (color) {
case Color::kDefault:
return bold ? "\u001b[1m" : "\u001b[0m";
case Color::kBlack:
return bold ? "\u001b[30;1m" : "\u001b[30m";
case Color::kRed:
return bold ? "\u001b[31;1m" : "\u001b[31m";
case Color::kGreen:
return bold ? "\u001b[32;1m" : "\u001b[32m";
case Color::kYellow:
return bold ? "\u001b[33;1m" : "\u001b[33m";
case Color::kBlue:
return bold ? "\u001b[34;1m" : "\u001b[34m";
case Color::kMagenta:
return bold ? "\u001b[35;1m" : "\u001b[35m";
case Color::kCyan:
return bold ? "\u001b[36;1m" : "\u001b[36m";
case Color::kWhite:
return bold ? "\u001b[37;1m" : "\u001b[37m";
}
return ""; // unreachable
}
void write_color(Color color, bool bold) {
if (use_colors) {
auto* code = color_code(color, bold);
fwrite(code, 1, strlen(code), file);
}
}
FILE* const file;
const bool use_colors;
};
} // namespace
std::unique_ptr<Printer> Printer::create(FILE* out, bool use_colors) {
return std::make_unique<PrinterLinux>(out, use_colors);
}
} // namespace diag
} // namespace tint

View File

@@ -0,0 +1,42 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstring>
#include "src/tint/diagnostic/printer.h"
namespace tint {
namespace diag {
namespace {
class PrinterOther : public Printer {
public:
explicit PrinterOther(FILE* f) : file(f) {}
void write(const std::string& str, const Style&) override {
fwrite(str.data(), 1, str.size(), file);
}
private:
FILE* file;
};
} // namespace
std::unique_ptr<Printer> Printer::create(FILE* out, bool) {
return std::make_unique<PrinterOther>(out);
}
} // namespace diag
} // namespace tint

View File

@@ -0,0 +1,98 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/diagnostic/printer.h"
#include "gtest/gtest.h"
namespace tint {
namespace diag {
namespace {
// Actually verifying that the expected colors are printed is exceptionally
// difficult as:
// a) The color emission varies by OS.
// b) The logic checks to see if the printer is writing to a terminal, making
// mocking hard.
// c) Actually probing what gets written to a FILE* is notoriously tricky.
//
// The least we can do is to exersice the code - which is what we do here.
// The test will print each of the colors, and can be examined with human
// eyeballs.
// This can be enabled or disabled with ENABLE_PRINTER_TESTS
#define ENABLE_PRINTER_TESTS 0
#if ENABLE_PRINTER_TESTS
using PrinterTest = testing::Test;
TEST_F(PrinterTest, WithColors) {
auto printer = Printer::create(stdout, true);
printer->write("Default", Style{Color::kDefault, false});
printer->write("Black", Style{Color::kBlack, false});
printer->write("Red", Style{Color::kRed, false});
printer->write("Green", Style{Color::kGreen, false});
printer->write("Yellow", Style{Color::kYellow, false});
printer->write("Blue", Style{Color::kBlue, false});
printer->write("Magenta", Style{Color::kMagenta, false});
printer->write("Cyan", Style{Color::kCyan, false});
printer->write("White", Style{Color::kWhite, false});
printf("\n");
}
TEST_F(PrinterTest, BoldWithColors) {
auto printer = Printer::create(stdout, true);
printer->write("Default", Style{Color::kDefault, true});
printer->write("Black", Style{Color::kBlack, true});
printer->write("Red", Style{Color::kRed, true});
printer->write("Green", Style{Color::kGreen, true});
printer->write("Yellow", Style{Color::kYellow, true});
printer->write("Blue", Style{Color::kBlue, true});
printer->write("Magenta", Style{Color::kMagenta, true});
printer->write("Cyan", Style{Color::kCyan, true});
printer->write("White", Style{Color::kWhite, true});
printf("\n");
}
TEST_F(PrinterTest, WithoutColors) {
auto printer = Printer::create(stdout, false);
printer->write("Default", Style{Color::kDefault, false});
printer->write("Black", Style{Color::kBlack, false});
printer->write("Red", Style{Color::kRed, false});
printer->write("Green", Style{Color::kGreen, false});
printer->write("Yellow", Style{Color::kYellow, false});
printer->write("Blue", Style{Color::kBlue, false});
printer->write("Magenta", Style{Color::kMagenta, false});
printer->write("Cyan", Style{Color::kCyan, false});
printer->write("White", Style{Color::kWhite, false});
printf("\n");
}
TEST_F(PrinterTest, BoldWithoutColors) {
auto printer = Printer::create(stdout, false);
printer->write("Default", Style{Color::kDefault, true});
printer->write("Black", Style{Color::kBlack, true});
printer->write("Red", Style{Color::kRed, true});
printer->write("Green", Style{Color::kGreen, true});
printer->write("Yellow", Style{Color::kYellow, true});
printer->write("Blue", Style{Color::kBlue, true});
printer->write("Magenta", Style{Color::kMagenta, true});
printer->write("Cyan", Style{Color::kCyan, true});
printer->write("White", Style{Color::kWhite, true});
printf("\n");
}
#endif // ENABLE_PRINTER_TESTS
} // namespace
} // namespace diag
} // namespace tint

View File

@@ -0,0 +1,113 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstring>
#include "src/tint/diagnostic/printer.h"
#define WIN32_LEAN_AND_MEAN 1
#include <Windows.h>
namespace tint {
namespace diag {
namespace {
struct ConsoleInfo {
HANDLE handle = INVALID_HANDLE_VALUE;
WORD default_attributes = 0;
operator bool() const { return handle != INVALID_HANDLE_VALUE; }
};
ConsoleInfo console_info(FILE* file) {
if (file == nullptr) {
return {};
}
ConsoleInfo console{};
if (file == stdout) {
console.handle = GetStdHandle(STD_OUTPUT_HANDLE);
} else if (file == stderr) {
console.handle = GetStdHandle(STD_ERROR_HANDLE);
} else {
return {};
}
CONSOLE_SCREEN_BUFFER_INFO info{};
if (GetConsoleScreenBufferInfo(console.handle, &info) == 0) {
return {};
}
console.default_attributes = info.wAttributes;
return console;
}
class PrinterWindows : public Printer {
public:
PrinterWindows(FILE* f, bool use_colors)
: file(f), console(console_info(use_colors ? f : nullptr)) {}
void write(const std::string& str, const Style& style) override {
write_color(style.color, style.bold);
fwrite(str.data(), 1, str.size(), file);
write_color(Color::kDefault, false);
}
private:
WORD attributes(Color color, bool bold) {
switch (color) {
case Color::kDefault:
return console.default_attributes;
case Color::kBlack:
return 0;
case Color::kRed:
return FOREGROUND_RED | (bold ? FOREGROUND_INTENSITY : 0);
case Color::kGreen:
return FOREGROUND_GREEN | (bold ? FOREGROUND_INTENSITY : 0);
case Color::kYellow:
return FOREGROUND_RED | FOREGROUND_GREEN |
(bold ? FOREGROUND_INTENSITY : 0);
case Color::kBlue:
return FOREGROUND_BLUE | (bold ? FOREGROUND_INTENSITY : 0);
case Color::kMagenta:
return FOREGROUND_RED | FOREGROUND_BLUE |
(bold ? FOREGROUND_INTENSITY : 0);
case Color::kCyan:
return FOREGROUND_GREEN | FOREGROUND_BLUE |
(bold ? FOREGROUND_INTENSITY : 0);
case Color::kWhite:
return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
(bold ? FOREGROUND_INTENSITY : 0);
}
return 0; // unreachable
}
void write_color(Color color, bool bold) {
if (console) {
SetConsoleTextAttribute(console.handle, attributes(color, bold));
fflush(file);
}
}
FILE* const file;
const ConsoleInfo console;
};
} // namespace
std::unique_ptr<Printer> Printer::create(FILE* out, bool use_colors) {
return std::make_unique<PrinterWindows>(out, use_colors);
}
} // namespace diag
} // namespace tint