Skip to content

Commit 93cc7c2

Browse files
authored
Merge pull request #1297 from Thirumalai-Shaktivel/extend_loc_manager_1
Extend Location manger to handle multi-file import errors
2 parents 2790b06 + 5196ecb commit 93cc7c2

24 files changed

+513
-280
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ share/jupyter/kernels/fortran/kernel.json
6262
src/runtime/*.o.empty.c
6363
python_ast.py
6464
python_ast.h
65-
ser.txt
65+
input.txt
6666
integration_tests/py_*
6767
integration_tests/b1/*
6868
integration_tests/b2/*
@@ -72,6 +72,7 @@ integration_tests/b5/*
7272
integration_tests/b6/*
7373
integration_tests/_lpython-tmp-test-*
7474
inst/bin/*
75+
*.tmp
7576
*.tlog
7677
*.filters
7778
*.obj

src/bin/lpython.cpp

Lines changed: 230 additions & 136 deletions
Large diffs are not rendered by default.

src/libasr/codegen/asr_to_llvm.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -375,9 +375,14 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
375375
void debug_get_line_column(const uint32_t &loc_first,
376376
uint32_t &line, uint32_t &column) {
377377
LocationManager lm;
378-
lm.in_filename = infile;
379-
lm.init_simple(LFortran::read_file(infile));
380-
lm.pos_to_linecol(lm.output_to_input_pos(loc_first, false), line, column);
378+
LocationManager::FileLocations fl;
379+
fl.in_filename = infile;
380+
lm.files.push_back(fl);
381+
std::string input = LFortran::read_file(infile);
382+
lm.init_simple(input);
383+
lm.file_ends.push_back(input.size());
384+
lm.pos_to_linecol(lm.output_to_input_pos(loc_first, false),
385+
line, column, fl.in_filename);
381386
}
382387

383388
template <typename T>

src/libasr/diagnostics.cpp

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,19 @@ bool Diagnostics::has_error() const {
6161
return false;
6262
}
6363

64-
std::string Diagnostics::render(const std::string &input,
65-
const LocationManager &lm, const CompilerOptions &compiler_options) {
64+
std::string Diagnostics::render(LocationManager &lm,
65+
const CompilerOptions &compiler_options) {
6666
std::string out;
6767
for (auto &d : this->diagnostics) {
6868
if (compiler_options.no_warnings && d.level != Level::Error) {
6969
continue;
7070
}
7171
if (compiler_options.error_format == "human") {
72-
out += render_diagnostic_human(d, input, lm,
73-
compiler_options.use_colors,
72+
out += render_diagnostic_human(d, lm, compiler_options.use_colors,
7473
compiler_options.show_stacktrace);
7574
if (&d != &this->diagnostics.back()) out += "\n";
7675
} else if (compiler_options.error_format == "short") {
77-
out += render_diagnostic_short(d, input, lm);
76+
out += render_diagnostic_short(d, lm);
7877
} else {
7978
throw LCompilersException("Error format not supported.");
8079
}
@@ -119,49 +118,47 @@ std::string get_line(std::string str, int n)
119118
return line;
120119
}
121120

122-
void populate_span(diag::Span &s, const LocationManager &lm,
123-
const std::string &input) {
121+
void populate_span(diag::Span &s, const LocationManager &lm) {
124122
lm.pos_to_linecol(lm.output_to_input_pos(s.loc.first, false),
125-
s.first_line, s.first_column);
123+
s.first_line, s.first_column, s.filename);
126124
lm.pos_to_linecol(lm.output_to_input_pos(s.loc.last, true),
127-
s.last_line, s.last_column);
128-
s.filename = lm.in_filename;
125+
s.last_line, s.last_column, s.filename);
126+
std::string input;
127+
read_file(s.filename, input);
129128
for (uint32_t i = s.first_line; i <= s.last_line; i++) {
130129
s.source_code.push_back(get_line(input, i));
131130
}
132131
LFORTRAN_ASSERT(s.source_code.size() > 0)
133132
}
134133

135134
// Loop over all labels and their spans, populate all of them
136-
void populate_spans(diag::Diagnostic &d, const LocationManager &lm,
137-
const std::string &input) {
135+
void populate_spans(diag::Diagnostic &d, const LocationManager &lm) {
138136
for (auto &l : d.labels) {
139137
for (auto &s : l.spans) {
140-
populate_span(s, lm, input);
138+
populate_span(s, lm);
141139
}
142140
}
143141
}
144142

145143
// Fills Diagnostic with span details and renders it
146-
std::string render_diagnostic_human(Diagnostic &d, const std::string &input,
147-
const LocationManager &lm, bool use_colors, bool show_stacktrace) {
144+
std::string render_diagnostic_human(Diagnostic &d, const LocationManager &lm,
145+
bool use_colors, bool show_stacktrace) {
148146
std::string out;
149147
if (show_stacktrace) {
150148
out += error_stacktrace(d.stacktrace);
151149
}
152150
// Convert to line numbers and get source code strings
153-
populate_spans(d, lm, input);
151+
populate_spans(d, lm);
154152
// Render the message
155153
out += render_diagnostic_human(d, use_colors);
156154
return out;
157155
}
158156

159157
// Fills Diagnostic with span details and renders it
160-
std::string render_diagnostic_short(Diagnostic &d, const std::string &input,
161-
const LocationManager &lm) {
158+
std::string render_diagnostic_short(Diagnostic &d, const LocationManager &lm) {
162159
std::string out;
163160
// Convert to line numbers and get source code strings
164-
populate_spans(d, lm, input);
161+
populate_spans(d, lm);
165162
// Render the message
166163
out += render_diagnostic_short(d);
167164
return out;
@@ -245,11 +242,24 @@ std::string render_diagnostic_human(const Diagnostic &d, bool use_colors) {
245242
}
246243
// and start a new one:
247244
s0 = s2;
245+
if (s0.filename != s.filename) {
246+
out << std::endl;
247+
// TODO: print the primary line+column here, not the first label:
248+
out << std::string(line_num_width, ' ') << blue_bold;
249+
out << "-->" << reset << " " << s0.filename << ":";
250+
out << s0.first_line << ":" << s0.first_column;
251+
if (s0.first_line != s0.last_line) {
252+
out << " - " << s0.last_line << ":" << s0.last_column;
253+
}
254+
out << std::endl;
255+
}
256+
248257
if (s0.first_line == s0.last_line) {
249258
out << std::string(line_num_width+1, ' ') << blue_bold << "|"
250259
<< reset << std::endl;
251260
std::string line = s0.source_code[0];
252261
std::replace(std::begin(line), std::end(line), '\t', ' ');
262+
line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
253263
out << blue_bold << std::setw(line_num_width)
254264
<< std::to_string(s0.first_line) << " |" << reset << " "
255265
<< line << std::endl;
@@ -263,6 +273,7 @@ std::string render_diagnostic_human(const Diagnostic &d, bool use_colors) {
263273
<< reset << std::endl;
264274
std::string line = s0.source_code[0];
265275
std::replace(std::begin(line), std::end(line), '\t', ' ');
276+
line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
266277
out << blue_bold << std::setw(line_num_width)
267278
<< std::to_string(s0.first_line) << " |" << reset << " "
268279
<< " " + line << std::endl;
@@ -281,6 +292,7 @@ std::string render_diagnostic_human(const Diagnostic &d, bool use_colors) {
281292
<< reset << std::endl;
282293
line = s0.source_code[s0.source_code.size()-1];
283294
std::replace(std::begin(line), std::end(line), '\t', ' ');
295+
line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
284296
out << blue_bold << std::setw(line_num_width)
285297
<< std::to_string(s0.last_line) << " |" << reset << " "
286298
<< " " + line << std::endl;

src/libasr/diagnostics.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,7 @@ struct Diagnostic {
115115
struct Diagnostics {
116116
std::vector<Diagnostic> diagnostics;
117117

118-
// Render nice error messages using all the information we have
119-
std::string render(const std::string &input,
120-
const LocationManager &lm, const CompilerOptions &compiler_options);
118+
std::string render(LocationManager &lm, const CompilerOptions &compiler_options);
121119

122120
// Renders the error message using only the information in Diagnostics
123121
std::string render2();
@@ -214,10 +212,9 @@ std::string render_diagnostic_human(const Diagnostic &d, bool use_colors);
214212
std::string render_diagnostic_short(const Diagnostic &d);
215213

216214
// Fills Diagnostic with span details and renders it
217-
std::string render_diagnostic_human(Diagnostic &d, const std::string &input,
218-
const LocationManager &lm, bool use_colors, bool show_stacktrace);
219-
std::string render_diagnostic_short(Diagnostic &d, const std::string &input,
220-
const LocationManager &lm);
215+
std::string render_diagnostic_human(Diagnostic &d, const LocationManager &lm,
216+
bool use_colors, bool show_stacktrace);
217+
std::string render_diagnostic_short(Diagnostic &d, const LocationManager &lm);
221218
/**
222219
* @brief Convert diagnostic `Level` i.e. severity to string and color accordingly.
223220
*

src/libasr/location.h

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -94,50 +94,64 @@ struct LocationManager {
9494
//
9595
//
9696
//
97-
std::vector<uint32_t> out_start; // consecutive intervals in the output code
98-
std::vector<uint32_t> in_start; // start + size in the original code
99-
std::vector<uint32_t> in_newlines; // position of all \n in the original code
97+
struct FileLocations {
98+
std::vector<uint32_t> out_start; // consecutive intervals in the output code
99+
std::vector<uint32_t> in_start; // start + size in the original code
100+
std::vector<uint32_t> in_newlines; // position of all \n in the original code
100101

101-
// For preprocessor (if preprocessor==true).
102-
// TODO: design a common structure, that also works with #include, that
103-
// has these mappings for each file
104-
bool preprocessor = false;
105-
std::string in_filename;
106-
uint32_t current_line=0;
107-
std::vector<uint32_t> out_start0; // consecutive intervals in the output code
108-
std::vector<uint32_t> in_start0; // start + size in the original code
109-
std::vector<uint32_t> in_size0; // Size of the `in` interval
110-
std::vector<uint32_t> interval_type0; // 0 .... 1:1; 1 ... many to many;
111-
std::vector<uint32_t> in_newlines0; // position of all \n in the original code
112-
// std::vector<uint32_t> filename_id; // file name for each interval, ID
113-
// std::vector<std::string> filenames; // filenames lookup for an ID
102+
// For preprocessor (if preprocessor==true).
103+
// TODO: design a common structure, that also works with #include, that
104+
// has these mappings for each file
105+
bool preprocessor = false;
106+
std::string in_filename;
107+
uint32_t current_line=0;
108+
std::vector<uint32_t> out_start0; // consecutive intervals in the output code
109+
std::vector<uint32_t> in_start0; // start + size in the original code
110+
std::vector<uint32_t> in_size0; // Size of the `in` interval
111+
std::vector<uint32_t> interval_type0; // 0 .... 1:1; 1 ... many to many;
112+
std::vector<uint32_t> in_newlines0; // position of all \n in the original code
113+
};
114+
std::vector<FileLocations> files;
115+
// Location is continued for the imported files, i.e.,if the new file is
116+
// imported then the location starts from the size of the previous file.
117+
// For example: file_ends = [120, 200, 350]
118+
std::vector<uint32_t> file_ends; // position of all ends of files
119+
// For a given Location, we use the `file_ends` and `bisection` to determine
120+
// the file (files' index), which the location is from. Then we use this index into
121+
// the `files` vector and use `in_newlines` and other information to
122+
// determine the line and column inside this file, and the `in_filename`
123+
// field to determine the filename. This happens when the diagnostic is
124+
// printed.
114125

115126
// Converts a position in the output code to a position in the original code
116127
// Every character in the output code has a corresponding location in the
117128
// original code, so this function always succeeds
118129
uint32_t output_to_input_pos(uint32_t out_pos, bool show_last) const {
119-
if (out_start.size() == 0) return 0;
120-
uint32_t interval = bisection(out_start, out_pos)-1;
121-
uint32_t rel_pos = out_pos - out_start[interval];
122-
uint32_t in_pos = in_start[interval] + rel_pos;
123-
if (preprocessor) {
130+
// Determine where the error is from using position, i.e., loc
131+
uint32_t index = bisection(file_ends, out_pos);
132+
if (index == file_ends.size()) index -= 1;
133+
if (files[index].out_start.size() == 0) return 0;
134+
uint32_t interval = bisection(files[index].out_start, out_pos)-1;
135+
uint32_t rel_pos = out_pos - files[index].out_start[interval];
136+
uint32_t in_pos = files[index].in_start[interval] + rel_pos;
137+
if (files[index].preprocessor) {
124138
// If preprocessor was used, do one more remapping
125-
uint32_t interval0 = bisection(out_start0, in_pos)-1;
126-
if (interval_type0[interval0] == 0) {
139+
uint32_t interval0 = bisection(files[index].out_start0, in_pos)-1;
140+
if (files[index].interval_type0[interval0] == 0) {
127141
// 1:1 interval
128-
uint32_t rel_pos0 = in_pos - out_start0[interval0];
129-
uint32_t in_pos0 = in_start0[interval0] + rel_pos0;
142+
uint32_t rel_pos0 = in_pos - files[index].out_start0[interval0];
143+
uint32_t in_pos0 = files[index].in_start0[interval0] + rel_pos0;
130144
return in_pos0;
131145
} else {
132146
// many to many interval
133147
uint32_t in_pos0;
134-
if (in_pos == out_start0[interval0+1]-1 || show_last) {
148+
if (in_pos == files[index].out_start0[interval0+1]-1 || show_last) {
135149
// The end of the interval in "out" code
136150
// Return the end of the interval in "in" code
137-
in_pos0 = in_start0[interval0]+in_size0[interval0]-1;
151+
in_pos0 = files[index].in_start0[interval0]+files[index].in_size0[interval0]-1;
138152
} else {
139153
// Otherwise return the beginning of the interval in "in"
140-
in_pos0 = in_start0[interval0];
154+
in_pos0 = files[index].in_start0[interval0];
141155
}
142156
return in_pos0;
143157
}
@@ -150,12 +164,19 @@ struct LocationManager {
150164
// `position` starts from 0
151165
// `line` and `col` starts from 1
152166
// `in_newlines` starts from 0
153-
void pos_to_linecol(uint32_t position, uint32_t &line, uint32_t &col) const {
167+
void pos_to_linecol(uint32_t position, uint32_t &line, uint32_t &col,
168+
std::string &filename) const {
169+
// Determine where the error is from using position, i.e., loc
170+
uint32_t index = bisection(file_ends, position);
171+
if (index == file_ends.size()) index -= 1;
172+
filename = files[index].in_filename;
173+
// Get the actual location by subtracting the previous file size.
174+
if (index > 0) position -= file_ends[index - 1];
154175
const std::vector<uint32_t> *newlines;
155-
if (preprocessor) {
156-
newlines = &in_newlines0;
176+
if (files[index].preprocessor) {
177+
newlines = &files[index].in_newlines0;
157178
} else {
158-
newlines = &in_newlines;
179+
newlines = &files[index].in_newlines;
159180
}
160181
int32_t interval = bisection(*newlines, position);
161182
if (interval >= 1 && position == (*newlines)[interval-1]) {
@@ -179,9 +200,9 @@ struct LocationManager {
179200

180201
void init_simple(const std::string &input) {
181202
uint32_t n = input.size();
182-
out_start = {0, n};
183-
in_start = {0, n};
184-
get_newlines(input, in_newlines);
203+
files.back().out_start = {0, n};
204+
files.back().in_start = {0, n};
205+
get_newlines(input, files.back().in_newlines);
185206
}
186207

187208
};

src/libasr/lsp_interface.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace LFortran::LPython {
1111
uint32_t first_column;
1212
uint32_t last_line;
1313
uint32_t last_column;
14+
std::string filename;
1415
uint32_t severity;
1516
};
1617
struct document_symbols {
@@ -19,6 +20,7 @@ namespace LFortran::LPython {
1920
uint32_t first_column;
2021
uint32_t last_line;
2122
uint32_t last_column;
23+
std::string filename;
2224
};
2325

2426
} // namespace LFortran::Python

src/lpython/parser/parser.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
namespace LFortran {
1616

1717
Result<LPython::AST::Module_t*> parse(Allocator &al, const std::string &s,
18-
diag::Diagnostics &diagnostics)
18+
uint32_t prev_loc, diag::Diagnostics &diagnostics)
1919
{
2020
Parser p(al, diagnostics);
2121
try {
22-
p.parse(s);
22+
p.parse(s, prev_loc);
2323
} catch (const parser_local::TokenizerError &e) {
2424
Error error;
2525
diagnostics.diagnostics.push_back(e.d);
@@ -42,15 +42,15 @@ Result<LPython::AST::Module_t*> parse(Allocator &al, const std::string &s,
4242
p.result.p, p.result.size(), p.type_ignore.p, p.type_ignore.size());
4343
}
4444

45-
void Parser::parse(const std::string &input)
45+
void Parser::parse(const std::string &input, uint32_t prev_loc)
4646
{
4747
inp = input;
4848
if (inp.size() > 0) {
4949
if (inp[inp.size()-1] != '\n') inp.append("\n");
5050
} else {
5151
inp.append("\n");
5252
}
53-
m_tokenizer.set_string(inp);
53+
m_tokenizer.set_string(inp, prev_loc);
5454
if (yyparse(*this) == 0) {
5555
return;
5656
}
@@ -116,13 +116,14 @@ Result<LPython::AST::ast_t*> parse_python_file(Allocator &al,
116116
const std::string &/*runtime_library_dir*/,
117117
const std::string &infile,
118118
diag::Diagnostics &diagnostics,
119+
uint32_t prev_loc,
119120
bool new_parser) {
120121
LPython::AST::ast_t* ast;
121122
// We will be using the new parser from now on
122123
new_parser = true;
123124
LFORTRAN_ASSERT(new_parser)
124125
std::string input = read_file(infile);
125-
Result<LPython::AST::Module_t*> res = parse(al, input, diagnostics);
126+
Result<LPython::AST::Module_t*> res = parse(al, input, prev_loc, diagnostics);
126127
if (res.ok) {
127128
ast = (LPython::AST::ast_t*)res.result;
128129
} else {

0 commit comments

Comments
 (0)