blob: 737e09dfc8bdec40cd9d778dc41c7e7c3d488d50 [file] [log] [blame]
// 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