Skip to content

Commit cfa70ca

Browse files
author
Christian Parpart
committed
liblangutil: refactor SourceReferenceFormatter into formatting-only and add SourceReferenceExtractor
1 parent 2f8a792 commit cfa70ca

File tree

5 files changed

+238
-65
lines changed

5 files changed

+238
-65
lines changed

liblangutil/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ set(sources
55
Exceptions.cpp
66
ParserBase.cpp
77
Scanner.cpp
8+
SourceReferenceExtractor.cpp
89
SourceReferenceFormatter.cpp
910
Token.cpp
1011
)
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
This file is part of solidity.
3+
4+
solidity is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
solidity is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
#include <liblangutil/SourceReferenceExtractor.h>
18+
#include <liblangutil/CharStream.h>
19+
#include <liblangutil/Exceptions.h>
20+
21+
#include <cmath>
22+
#include <iomanip>
23+
24+
using namespace std;
25+
using namespace dev;
26+
using namespace langutil;
27+
28+
/* TODO
29+
* - see if we can get rid of the GETTER fn by adding that functionality to SourceLocation
30+
* - SourceLocation: sourceName* with CharStream&
31+
* - CharStream: add sourceName*
32+
* - Scanner: remove m_sourceName
33+
* - base SourceReferenceFormatter on top of that logic
34+
* - make sure tests are running
35+
* - write tests for SourceReferenceExtractor
36+
* - Scanner::reset(CharStream _source, string _sourceName): drop 2nd param
37+
*/
38+
39+
SourceReferenceExtractor::Message SourceReferenceExtractor::extract(
40+
Exception const& _exception,
41+
string _category,
42+
CharStreamGetter _getter
43+
)
44+
{
45+
SourceReferenceExtractor extractor{std::move(_getter)};
46+
return extractor.extract(_exception, std::move(_category));
47+
}
48+
49+
SourceReference SourceReferenceExtractor::extract(
50+
SourceLocation const* _location,
51+
string description,
52+
CharStreamGetter _getter
53+
)
54+
{
55+
return SourceReferenceExtractor{std::move(_getter)}.extract(_location, std::move(description));
56+
}
57+
58+
SourceReferenceExtractor::Message SourceReferenceExtractor::extract(
59+
Exception const& _exception,
60+
string _category
61+
)
62+
{
63+
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
64+
65+
string const* description = boost::get_error_info<errinfo_comment>(_exception);
66+
SourceReference primary = extract(location, description ? *description : "");
67+
68+
std::vector<SourceReference> secondary;
69+
auto secondaryLocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
70+
if (secondaryLocation && !secondaryLocation->infos.empty())
71+
for (auto const& info: secondaryLocation->infos)
72+
secondary.emplace_back(extract(&info.second, info.first));
73+
74+
return Message{std::move(primary), _category, std::move(secondary)};
75+
}
76+
77+
SourceReference SourceReferenceExtractor::extract(SourceLocation const* _location, std::string description)
78+
{
79+
if (!_location || !_location->hasSourceName()) // Nothing we can extract here
80+
return SourceReference{"", "", 0, 0, 0, 0, 0, std::move(description)};
81+
82+
auto const& source = m_charStreamGetter(_location->sourceName());
83+
84+
int startLine;
85+
int startColumn;
86+
tie(startLine, startColumn) = source.translatePositionToLineColumn(_location->start);
87+
88+
int endLine;
89+
int endColumn;
90+
tie(endLine, endColumn) = source.translatePositionToLineColumn(_location->end);
91+
92+
string line = source.lineAtPosition(_location->start);
93+
94+
int locationLength = endColumn - startColumn;
95+
if (locationLength > 150)
96+
{
97+
line = line.substr(0, startColumn + 35) + " ... " + line.substr(endColumn - 35);
98+
endColumn = startColumn + 75;
99+
locationLength = 75;
100+
}
101+
102+
if (line.length() > 150)
103+
{
104+
int len = line.length();
105+
line = line.substr(max(0, startColumn - 35), min(startColumn, 35) + min(locationLength + 35, len - startColumn));
106+
if (startColumn + locationLength + 35 < len)
107+
line += " ...";
108+
if (startColumn > 35)
109+
{
110+
line = " ... " + line;
111+
startColumn = 40;
112+
}
113+
endColumn = startColumn + locationLength;
114+
}
115+
116+
return SourceReference{
117+
_location->sourceName(),
118+
line,
119+
startLine,
120+
startColumn,
121+
endLine,
122+
endColumn,
123+
locationLength,
124+
std::move(description),
125+
};
126+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
This file is part of solidity.
3+
4+
solidity is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
solidity is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
#pragma once
18+
19+
#include <string>
20+
#include <vector>
21+
#include <functional>
22+
23+
namespace dev
24+
{
25+
struct Exception;
26+
}
27+
28+
namespace langutil
29+
{
30+
31+
struct SourceReference
32+
{
33+
std::string filename;
34+
std::string line;
35+
int startLine;
36+
int startColumn;
37+
int endLine;
38+
int endColumn;
39+
int locationLength;
40+
std::string description;
41+
};
42+
43+
class CharStream;
44+
struct SourceLocation;
45+
46+
class SourceReferenceExtractor
47+
{
48+
public:
49+
using CharStreamGetter = std::function<CharStream const&(std::string const /*filename*/&)>;
50+
51+
struct Message
52+
{
53+
SourceReference primary;
54+
std::string category; // "Error", "Warning", ... (TODO: use an enum class)
55+
std::vector<SourceReference> secondary;
56+
};
57+
58+
explicit SourceReferenceExtractor(CharStreamGetter getter): m_charStreamGetter{std::move(getter)} {}
59+
SourceReferenceExtractor(SourceReferenceExtractor const&) = default;
60+
SourceReferenceExtractor(SourceReferenceExtractor&&) = default;
61+
SourceReferenceExtractor& operator=(SourceReferenceExtractor const&) = default;
62+
SourceReferenceExtractor& operator=(SourceReferenceExtractor&&) = default;
63+
~SourceReferenceExtractor() = default;
64+
65+
static Message extract(dev::Exception const& _exception, std::string _category, CharStreamGetter _getter);
66+
static SourceReference extract(SourceLocation const* _location, std::string description, CharStreamGetter _getter);
67+
68+
Message extract(dev::Exception const& _exception, std::string _category);
69+
SourceReference extract(SourceLocation const* _location, std::string description = "");
70+
71+
private:
72+
CharStreamGetter m_charStreamGetter;
73+
};
74+
75+
}

liblangutil/SourceReferenceFormatter.cpp

Lines changed: 32 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222

2323
#include <liblangutil/SourceReferenceFormatter.h>
24+
#include <liblangutil/SourceReferenceExtractor.h>
2425
#include <liblangutil/Scanner.h>
2526
#include <liblangutil/Exceptions.h>
2627

@@ -30,100 +31,68 @@ using namespace langutil;
3031

3132
void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location)
3233
{
33-
if (!_location || !_location->hasSourceName())
34-
return; // Nothing we can print here
35-
auto const& scanner = m_scannerFromSourceName(_location->sourceName());
36-
int startLine;
37-
int startColumn;
38-
tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start);
39-
int endLine;
40-
int endColumn;
41-
tie(endLine, endColumn) = scanner.translatePositionToLineColumn(_location->end);
42-
if (startLine == endLine)
43-
{
44-
string line = scanner.lineAtPosition(_location->start);
34+
auto resolver = [this](string const& filename) -> CharStream const& { return m_scannerFromSourceName(filename).charStream(); };
35+
printSourceLocation(SourceReferenceExtractor::extract(_location, "", resolver));
36+
}
4537

46-
int locationLength = endColumn - startColumn;
47-
if (locationLength > 150)
48-
{
49-
line = line.substr(0, startColumn + 35) + " ... " + line.substr(endColumn - 35);
50-
endColumn = startColumn + 75;
51-
locationLength = 75;
52-
}
53-
if (line.length() > 150)
54-
{
55-
int len = line.length();
56-
line = line.substr(max(0, startColumn - 35), min(startColumn, 35) + min(locationLength + 35, len - startColumn));
57-
if (startColumn + locationLength + 35 < len)
58-
line += " ...";
59-
if (startColumn > 35)
60-
{
61-
line = " ... " + line;
62-
startColumn = 40;
63-
}
64-
endColumn = startColumn + locationLength;
65-
}
38+
void SourceReferenceFormatter::printSourceLocation(SourceReference const& _ref)
39+
{
40+
if (_ref.startLine == 0 || _ref.filename.empty())
41+
return; // Nothing we can print here
6642

67-
m_stream << line << endl;
43+
if (_ref.startLine == _ref.endLine)
44+
{
45+
m_stream << _ref.line << endl;
6846

6947
for_each(
70-
line.cbegin(),
71-
line.cbegin() + startColumn,
48+
_ref.line.cbegin(),
49+
_ref.line.cbegin() + _ref.startColumn,
7250
[this](char const& ch) { m_stream << (ch == '\t' ? '\t' : ' '); }
7351
);
7452
m_stream << "^";
75-
if (endColumn > startColumn + 2)
76-
m_stream << string(endColumn - startColumn - 2, '-');
77-
if (endColumn > startColumn + 1)
53+
if (_ref.endColumn > _ref.startColumn + 2)
54+
m_stream << string(_ref.endColumn - _ref.startColumn - 2, '-');
55+
if (_ref.endColumn > _ref.startColumn + 1)
7856
m_stream << "^";
7957
m_stream << endl;
8058
}
8159
else
8260
m_stream <<
83-
scanner.lineAtPosition(_location->start) <<
61+
_ref.line <<
8462
endl <<
85-
string(startColumn, ' ') <<
63+
string(_ref.startColumn, ' ') <<
8664
"^ (Relevant source part starts here and spans across multiple lines)." <<
8765
endl;
8866
}
8967

90-
void SourceReferenceFormatter::printSourceName(SourceLocation const* _location)
68+
void SourceReferenceFormatter::printSourceName(SourceReference const& _ref)
9169
{
92-
if (!_location || !_location->hasSourceName())
93-
return; // Nothing we can print here
94-
auto const& scanner = m_scannerFromSourceName(_location->sourceName());
95-
int startLine;
96-
int startColumn;
97-
tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start);
98-
m_stream << _location->sourceName() << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": ";
70+
if (!_ref.filename.empty())
71+
m_stream << _ref.filename << ":" << (_ref.startLine + 1) << ":" << (_ref.startColumn + 1) << ": ";
9972
}
10073

10174
void SourceReferenceFormatter::printExceptionInformation(
10275
dev::Exception const& _exception,
10376
string const& _name
10477
)
10578
{
106-
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
107-
auto secondarylocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
79+
auto resolver = [this](string const& filename) -> CharStream const& { return m_scannerFromSourceName(filename).charStream(); };
80+
auto const ref = SourceReferenceExtractor::extract(_exception, _name, resolver);
10881

109-
printSourceName(location);
82+
printSourceName(ref.primary);
11083

111-
m_stream << _name;
112-
if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
113-
m_stream << ": " << *description << endl;
84+
m_stream << ref.category;
85+
if (!ref.primary.description.empty())
86+
m_stream << ": " << ref.primary.description << endl;
11487
else
11588
m_stream << endl;
11689

117-
printSourceLocation(location);
90+
printSourceLocation(ref.primary);
11891

119-
if (secondarylocation && !secondarylocation->infos.empty())
92+
for (auto const& secondary: ref.secondary)
12093
{
121-
for (auto info: secondarylocation->infos)
122-
{
123-
printSourceName(&info.second);
124-
m_stream << info.first << endl;
125-
printSourceLocation(&info.second);
126-
}
127-
m_stream << endl;
94+
printSourceName(secondary);
95+
printSourceLocation(secondary);
12896
}
97+
m_stream << endl;
12998
}

liblangutil/SourceReferenceFormatter.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct Exception; // forward
3434
namespace langutil
3535
{
3636
struct SourceLocation;
37+
struct SourceReference;
3738
class Scanner;
3839

3940
class SourceReferenceFormatter
@@ -50,7 +51,8 @@ class SourceReferenceFormatter
5051
{}
5152

5253
/// Prints source location if it is given.
53-
void printSourceLocation(langutil::SourceLocation const* _location);
54+
void printSourceLocation(SourceLocation const* _location);
55+
void printSourceLocation(SourceReference const& ref);
5456
void printExceptionInformation(dev::Exception const& _exception, std::string const& _name);
5557

5658
static std::string formatExceptionInformation(
@@ -67,7 +69,7 @@ class SourceReferenceFormatter
6769
}
6870
private:
6971
/// Prints source name if location is given.
70-
void printSourceName(langutil::SourceLocation const* _location);
72+
void printSourceName(SourceReference const& ref);
7173

7274
std::ostream& m_stream;
7375
ScannerFromSourceNameFun m_scannerFromSourceName;

0 commit comments

Comments
 (0)