1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "fidl/error_reporter.h"
6#include "fidl/source_location.h"
7#include "fidl/string_view.h"
8#include "fidl/token.h"
9
10namespace fidl {
11
12std::string MakeSquiggle(const std::string& surrounding_line, int column) {
13    std::string squiggle;
14    for (int i = 0; i < column; i++) {
15        switch (surrounding_line[i]) {
16        case '\t':
17            squiggle.push_back('\t');
18        default:
19            squiggle.push_back(' ');
20        }
21    }
22    squiggle.push_back('^');
23    return squiggle;
24}
25
26std::string FormatError(const SourceLocation& location, StringView message, size_t squiggle_size = 0u) {
27    SourceFile::Position position;
28    std::string surrounding_line = location.SourceLine(&position);
29
30    std::string squiggle = MakeSquiggle(surrounding_line, position.column);
31    if (squiggle_size != 0u) {
32        --squiggle_size;
33    }
34    squiggle += std::string(squiggle_size, '~');
35    // Some tokens (like string literals) can span multiple
36    // lines. Truncate the string to just one line at most. The
37    // containing line contains a newline, so drop it when
38    // comparing sizes.
39    size_t line_size = surrounding_line.size() - 1;
40    if (squiggle.size() > line_size) {
41        squiggle.resize(line_size);
42    }
43
44    // Many editors and IDEs recognize errors in the form of
45    // filename:linenumber:column: error: descriptive-test-here\n
46    std::string error = location.position();
47    error.append(": error: ");
48    error.append(message);
49    error.push_back('\n');
50    error.append(surrounding_line);
51    error.append(squiggle);
52
53    return error;
54}
55
56// ReportError records an error with the location, message, source line, and
57// position indicator.
58//
59//     filename:line:col: error: message
60//     sourceline
61//        ^
62void ErrorReporter::ReportError(const SourceLocation& location, StringView message) {
63    auto error = FormatError(location, message);
64    errors_.push_back(std::move(error));
65}
66
67// ReportError records an error with the location, message, source line,
68// position indicator, and tildes under the token reported.
69//
70//     filename:line:col: error: message
71//     sourceline
72//        ^~~~
73void ErrorReporter::ReportError(const Token& token, StringView message) {
74    auto token_location = token.location();
75    auto token_data = token_location.data();
76    auto error = FormatError(token_location, message, token_data.size());
77    errors_.push_back(std::move(error));
78}
79
80// ReportError records the provided message.
81void ErrorReporter::ReportError(StringView message) {
82    std::string error("error: ");
83    error.append(message);
84    errors_.push_back(std::move(error));
85}
86
87void ErrorReporter::PrintReports() {
88    for (const auto& error : errors_) {
89        fprintf(stderr, "%s\n", error.data());
90    }
91}
92
93} // namespace fidl
94