Add tint::diag namespace for diagnostics
Diagnostics will be used for printing parser / validator error mesasges. Diagnostics are collected into a `diag::List`, and can then be formatted into a human readable message with `diag::Formatter`. Bug: tint:282 Change-Id: I8bbef3db22b72d62cb9467c878d9a346890589ad Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31480 Commit-Queue: Ben Clayton <bclayton@google.com> Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
parent
580d6c7f3e
commit
f0740ae0f2
5
BUILD.gn
5
BUILD.gn
|
@ -377,6 +377,10 @@ source_set("libtint_core_src") {
|
||||||
"src/ast/workgroup_decoration.h",
|
"src/ast/workgroup_decoration.h",
|
||||||
"src/context.cc",
|
"src/context.cc",
|
||||||
"src/context.h",
|
"src/context.h",
|
||||||
|
"src/diagnostic/diagnostic.cc",
|
||||||
|
"src/diagnostic/diagnostic.h",
|
||||||
|
"src/diagnostic/formatter.cc",
|
||||||
|
"src/diagnostic/formatter.h",
|
||||||
"src/inspector/entry_point.cc",
|
"src/inspector/entry_point.cc",
|
||||||
"src/inspector/entry_point.h",
|
"src/inspector/entry_point.h",
|
||||||
"src/inspector/inspector.cc",
|
"src/inspector/inspector.cc",
|
||||||
|
@ -767,6 +771,7 @@ source_set("tint_unittests_core_src") {
|
||||||
"src/ast/variable_decl_statement_test.cc",
|
"src/ast/variable_decl_statement_test.cc",
|
||||||
"src/ast/variable_test.cc",
|
"src/ast/variable_test.cc",
|
||||||
"src/ast/workgroup_decoration_test.cc",
|
"src/ast/workgroup_decoration_test.cc",
|
||||||
|
"src/diagnostic/formatter_test.cc",
|
||||||
"src/inspector/inspector_test.cc",
|
"src/inspector/inspector_test.cc",
|
||||||
"src/scope_stack_test.cc",
|
"src/scope_stack_test.cc",
|
||||||
"src/transform/bound_array_accessors_transform_test.cc",
|
"src/transform/bound_array_accessors_transform_test.cc",
|
||||||
|
|
|
@ -198,6 +198,10 @@ set(TINT_LIB_SRCS
|
||||||
ast/workgroup_decoration.h
|
ast/workgroup_decoration.h
|
||||||
context.cc
|
context.cc
|
||||||
context.h
|
context.h
|
||||||
|
diagnostic/diagnostic.cc
|
||||||
|
diagnostic/diagnostic.h
|
||||||
|
diagnostic/formatter.cc
|
||||||
|
diagnostic/formatter.h
|
||||||
inspector/entry_point.cc
|
inspector/entry_point.cc
|
||||||
inspector/entry_point.h
|
inspector/entry_point.h
|
||||||
inspector/inspector.cc
|
inspector/inspector.cc
|
||||||
|
@ -377,6 +381,7 @@ set(TINT_TEST_SRCS
|
||||||
ast/variable_decl_statement_test.cc
|
ast/variable_decl_statement_test.cc
|
||||||
ast/variable_test.cc
|
ast/variable_test.cc
|
||||||
ast/workgroup_decoration_test.cc
|
ast/workgroup_decoration_test.cc
|
||||||
|
diagnostic/formatter_test.cc
|
||||||
inspector/inspector_test.cc
|
inspector/inspector_test.cc
|
||||||
scope_stack_test.cc
|
scope_stack_test.cc
|
||||||
transform/bound_array_accessors_transform_test.cc
|
transform/bound_array_accessors_transform_test.cc
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
// 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/diagnostic/diagnostic.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace diag {
|
||||||
|
|
||||||
|
List::List() = default;
|
||||||
|
List::List(std::initializer_list<Diagnostic> list) : entries_(list) {}
|
||||||
|
List::~List() = default;
|
||||||
|
|
||||||
|
} // namespace diag
|
||||||
|
} // namespace tint
|
|
@ -0,0 +1,79 @@
|
||||||
|
// 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_DIAGNOSTIC_DIAGNOSTIC_H_
|
||||||
|
#define SRC_DIAGNOSTIC_DIAGNOSTIC_H_
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/source.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace diag {
|
||||||
|
|
||||||
|
/// Severity is an enumerator of diagnostic severities.
|
||||||
|
enum class Severity { Info, Warning, Error, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Diagnostic holds all the information for a single compiler diagnostic
|
||||||
|
/// message.
|
||||||
|
class Diagnostic {
|
||||||
|
public:
|
||||||
|
Severity severity = Severity::Error;
|
||||||
|
Source source;
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// List is a container of Diagnostic messages.
|
||||||
|
class List {
|
||||||
|
public:
|
||||||
|
using iterator = std::vector<Diagnostic>::const_iterator;
|
||||||
|
|
||||||
|
List();
|
||||||
|
List(std::initializer_list<Diagnostic> list);
|
||||||
|
~List();
|
||||||
|
|
||||||
|
void add(Diagnostic&& diag) {
|
||||||
|
entries_.emplace_back(std::move(diag));
|
||||||
|
if (diag.severity >= Severity::Error) {
|
||||||
|
contains_errors_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @returns true iff the diagnostic list contains errors diagnostics (or of
|
||||||
|
/// higher severity).
|
||||||
|
bool contains_errors() const { return contains_errors_; }
|
||||||
|
/// @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(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Diagnostic> entries_;
|
||||||
|
bool contains_errors_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace diag
|
||||||
|
} // namespace tint
|
||||||
|
|
||||||
|
#endif // SRC_DIAGNOSTIC_DIAGNOSTIC_H_
|
|
@ -0,0 +1,152 @@
|
||||||
|
// 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/diagnostic/formatter.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "src/diagnostic/diagnostic.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace diag {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& operator<<(
|
||||||
|
std::basic_ostream<CharT, Traits>& stream,
|
||||||
|
Severity severity) {
|
||||||
|
switch (severity) {
|
||||||
|
case Severity::Info:
|
||||||
|
stream << "info";
|
||||||
|
break;
|
||||||
|
case Severity::Warning:
|
||||||
|
stream << "warning";
|
||||||
|
break;
|
||||||
|
case Severity::Error:
|
||||||
|
stream << "error";
|
||||||
|
break;
|
||||||
|
case Severity::Fatal:
|
||||||
|
stream << "fatal";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& operator<<(
|
||||||
|
std::basic_ostream<CharT, Traits>& stream,
|
||||||
|
const Source::Location& location) {
|
||||||
|
if (location.line > 0) {
|
||||||
|
stream << location.line;
|
||||||
|
if (location.column > 0) {
|
||||||
|
stream << ":" << location.column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BasicFormatter : public Formatter {
|
||||||
|
public:
|
||||||
|
BasicFormatter(bool print_file, bool print_severity, bool print_line)
|
||||||
|
: print_file_(print_file),
|
||||||
|
print_severity_(print_severity),
|
||||||
|
print_line_(print_line) {}
|
||||||
|
|
||||||
|
std::string format(const List& list) const override {
|
||||||
|
bool first = true;
|
||||||
|
std::stringstream ss;
|
||||||
|
for (auto diag : list) {
|
||||||
|
if (!first) {
|
||||||
|
ss << std::endl;
|
||||||
|
}
|
||||||
|
format(diag, ss);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void format(const Diagnostic& diag, std::stringstream& ss) const {
|
||||||
|
auto const& src = diag.source;
|
||||||
|
auto const& rng = src.range;
|
||||||
|
|
||||||
|
if (print_file_ && src.file != nullptr && !src.file->path.empty()) {
|
||||||
|
ss << src.file->path;
|
||||||
|
if (rng.begin.line > 0) {
|
||||||
|
ss << ":" << rng.begin;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ss << rng.begin;
|
||||||
|
}
|
||||||
|
if (print_severity_) {
|
||||||
|
ss << " " << diag.severity;
|
||||||
|
}
|
||||||
|
ss << ": " << diag.message;
|
||||||
|
|
||||||
|
if (print_line_ && src.file != nullptr && rng.begin.line > 0) {
|
||||||
|
ss << std::endl;
|
||||||
|
for (size_t line = rng.begin.line; line <= rng.end.line; line++) {
|
||||||
|
if (line < src.file->lines.size() + 1) {
|
||||||
|
auto len = src.file->lines[line - 1].size();
|
||||||
|
|
||||||
|
ss << src.file->lines[line - 1];
|
||||||
|
ss << std::endl;
|
||||||
|
|
||||||
|
if (line == rng.begin.line && line == rng.end.line) {
|
||||||
|
// Single line
|
||||||
|
repeat(' ', rng.begin.column - 1, ss);
|
||||||
|
repeat('^', std::max<size_t>(rng.end.column - rng.begin.column, 1),
|
||||||
|
ss);
|
||||||
|
} else if (line == rng.begin.line) {
|
||||||
|
// Start of multi-line
|
||||||
|
repeat(' ', rng.begin.column - 1, ss);
|
||||||
|
repeat('^', len - (rng.begin.column - 1), ss);
|
||||||
|
} else if (line == rng.end.line) {
|
||||||
|
// End of multi-line
|
||||||
|
repeat('^', rng.end.column - 1, ss);
|
||||||
|
} else {
|
||||||
|
// Middle of multi-line
|
||||||
|
repeat('^', len, ss);
|
||||||
|
}
|
||||||
|
ss << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void repeat(char c, size_t n, std::stringstream& ss) const {
|
||||||
|
while (n-- > 0) {
|
||||||
|
ss << c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool print_file_ = false;
|
||||||
|
const bool print_severity_ = false;
|
||||||
|
const bool print_line_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // 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;
|
||||||
|
|
||||||
|
} // namespace diag
|
||||||
|
} // namespace tint
|
|
@ -0,0 +1,47 @@
|
||||||
|
// 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_DIAGNOSTIC_FORMATTER_H_
|
||||||
|
#define SRC_DIAGNOSTIC_FORMATTER_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace diag {
|
||||||
|
|
||||||
|
class List;
|
||||||
|
|
||||||
|
/// Formatters are used to format a list of diagnostics into a human readable
|
||||||
|
/// string.
|
||||||
|
class Formatter {
|
||||||
|
public:
|
||||||
|
/// @returns a basic diagnostic formatter
|
||||||
|
/// @param print_file include the file path for each diagnostic
|
||||||
|
/// @param print_severity include the severity for each diagnostic
|
||||||
|
/// @param print_line include the source line(s) for the diagnostic
|
||||||
|
static std::unique_ptr<Formatter> create(bool print_file,
|
||||||
|
bool print_severity,
|
||||||
|
bool print_line);
|
||||||
|
|
||||||
|
virtual ~Formatter();
|
||||||
|
|
||||||
|
/// @return the human readable list of diagnostics formatted to a string.
|
||||||
|
virtual std::string format(const List&) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace diag
|
||||||
|
} // namespace tint
|
||||||
|
|
||||||
|
#endif // SRC_DIAGNOSTIC_FORMATTER_H_
|
|
@ -0,0 +1,140 @@
|
||||||
|
// 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/diagnostic/formatter.h"
|
||||||
|
#include "src/diagnostic/diagnostic.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace diag {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr const char* content =
|
||||||
|
R"(the cat says meow
|
||||||
|
the dog says woof
|
||||||
|
the snake says quack
|
||||||
|
the snail says ???
|
||||||
|
)";
|
||||||
|
|
||||||
|
class DiagFormatterTest : public testing::Test {
|
||||||
|
public:
|
||||||
|
Source::File file{"file.name", content};
|
||||||
|
Diagnostic diag_info{Severity::Info,
|
||||||
|
Source{Source::Range{Source::Location{1, 14}}, &file},
|
||||||
|
"purr"};
|
||||||
|
Diagnostic diag_warn{Severity::Warning,
|
||||||
|
Source{Source::Range{{2, 14}, {2, 18}}, &file}, "grrr"};
|
||||||
|
Diagnostic diag_err{Severity::Error,
|
||||||
|
Source{Source::Range{{3, 16}, {3, 21}}, &file}, "hiss"};
|
||||||
|
Diagnostic diag_fatal{Severity::Fatal,
|
||||||
|
Source{Source::Range{{4, 16}, {4, 19}}, &file},
|
||||||
|
"nothing"};
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(DiagFormatterTest, Simple) {
|
||||||
|
auto fmt = Formatter::create(false, false, false);
|
||||||
|
auto got = fmt->format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
||||||
|
auto* expect = R"(1:14: purr
|
||||||
|
2:14: grrr
|
||||||
|
3:16: hiss
|
||||||
|
4:16: nothing)";
|
||||||
|
ASSERT_EQ(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DiagFormatterTest, WithFile) {
|
||||||
|
auto fmt = Formatter::create(true, false, false);
|
||||||
|
auto got = fmt->format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
||||||
|
auto* expect = R"(file.name:1:14: purr
|
||||||
|
file.name:2:14: grrr
|
||||||
|
file.name:3:16: hiss
|
||||||
|
file.name:4:16: nothing)";
|
||||||
|
ASSERT_EQ(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DiagFormatterTest, WithSeverity) {
|
||||||
|
auto fmt = Formatter::create(false, true, false);
|
||||||
|
auto got = fmt->format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
||||||
|
auto* expect = R"(1:14 info: purr
|
||||||
|
2:14 warning: grrr
|
||||||
|
3:16 error: hiss
|
||||||
|
4:16 fatal: nothing)";
|
||||||
|
ASSERT_EQ(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DiagFormatterTest, WithLine) {
|
||||||
|
auto fmt = Formatter::create(false, false, true);
|
||||||
|
auto got = fmt->format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
||||||
|
auto* expect = R"(1:14: purr
|
||||||
|
the cat says meow
|
||||||
|
^
|
||||||
|
|
||||||
|
2:14: grrr
|
||||||
|
the dog says woof
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
3:16: hiss
|
||||||
|
the snake says quack
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
4:16: nothing
|
||||||
|
the snail says ???
|
||||||
|
^^^
|
||||||
|
)";
|
||||||
|
ASSERT_EQ(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DiagFormatterTest, BasicWithFileSeverityLine) {
|
||||||
|
auto fmt = Formatter::create(true, true, true);
|
||||||
|
auto got = fmt->format(List{diag_info, diag_warn, diag_err, diag_fatal});
|
||||||
|
auto* expect = R"(file.name:1:14 info: purr
|
||||||
|
the cat says meow
|
||||||
|
^
|
||||||
|
|
||||||
|
file.name:2:14 warning: grrr
|
||||||
|
the dog says woof
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
file.name:3:16 error: hiss
|
||||||
|
the snake says quack
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
file.name:4:16 fatal: nothing
|
||||||
|
the snail says ???
|
||||||
|
^^^
|
||||||
|
)";
|
||||||
|
ASSERT_EQ(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DiagFormatterTest, BasicWithMultiLine) {
|
||||||
|
Diagnostic multiline{Severity::Warning,
|
||||||
|
Source{Source::Range{{2, 9}, {4, 15}}, &file},
|
||||||
|
"multiline"};
|
||||||
|
|
||||||
|
auto fmt = Formatter::create(false, false, true);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace diag
|
||||||
|
} // namespace tint
|
Loading…
Reference in New Issue