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 "banjo/error_reporter.h"
6 #include "banjo/source_location.h"
7 #include "banjo/string_view.h"
8 #include "banjo/token.h"
9 
10 namespace banjo {
11 
MakeSquiggle(const std::string & surrounding_line,int column)12 std::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 
FormatError(const SourceLocation & location,StringView message,size_t squiggle_size=0u)26 std::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 //        ^
ReportError(const SourceLocation & location,StringView message)62 void 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 //        ^~~~
ReportError(const Token & token,StringView message)73 void 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.
ReportError(StringView message)81 void ErrorReporter::ReportError(StringView message) {
82     std::string error("error: ");
83     error.append(message);
84     errors_.push_back(std::move(error));
85 }
86 
PrintReports()87 void ErrorReporter::PrintReports() {
88     for (const auto& error : errors_) {
89         fprintf(stderr, "%s\n", error.data());
90     }
91 }
92 
93 } // namespace banjo
94