Skip to content

Commit 3d4b0f4

Browse files
Christian Parpartchriseth
Christian Parpart
authored andcommitted
liblangutil: refactors SourceReferenceFormatter error formatting for pretty and colored output.
* Refactors output format in a way it is (or should at least be) more readable. (NB.: As source of inspiration, I chose the rustc compiler output.) * Adds color support to the stream output. * Also improves multiline source formatting (i.e. truncating too long lines, like done with single lines already) * solc: adds flags --color (force terminal colors) and --no-color (disable autodetection) * solc: adds --new-reporter to give output in *new* formatting (colored or not) * Changelog adapted accordingly.
1 parent ef6a76c commit 3d4b0f4

7 files changed

+264
-12
lines changed

Changelog.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ Language Features:
1313

1414
Compiler Features:
1515
* C API (``libsolc`` / raw ``soljson.js``): Introduce ``solidity_free`` method which releases all internal buffers to save memory.
16-
16+
* Commandline interface: Adds new option ``--new-reporter`` for improved diagnostics formatting
17+
along with ``--color`` and ``--no-color`` for colorized output to be forced (or explicitly disabled).
1718

1819
Bugfixes:
1920

liblangutil/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ set(sources
1616
SourceReferenceExtractor.h
1717
SourceReferenceFormatter.cpp
1818
SourceReferenceFormatter.h
19+
SourceReferenceFormatterHuman.cpp
20+
SourceReferenceFormatterHuman.h
1921
Token.cpp
2022
Token.h
2123
UndefMacros.h

liblangutil/SourceReferenceFormatter.h

+8-5
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,14 @@ class SourceReferenceFormatter
4444
m_stream(_stream)
4545
{}
4646

47+
virtual ~SourceReferenceFormatter() = default;
48+
4749
/// Prints source location if it is given.
48-
void printSourceLocation(SourceLocation const* _location);
49-
void printSourceLocation(SourceReference const& _ref);
50-
void printExceptionInformation(dev::Exception const& _error, std::string const& _category);
51-
void printExceptionInformation(SourceReferenceExtractor::Message const& _msg);
50+
virtual void printSourceLocation(SourceReference const& _ref);
51+
virtual void printExceptionInformation(SourceReferenceExtractor::Message const& _msg);
52+
53+
virtual void printSourceLocation(SourceLocation const* _location);
54+
virtual void printExceptionInformation(dev::Exception const& _error, std::string const& _category);
5255

5356
static std::string formatExceptionInformation(
5457
dev::Exception const& _exception,
@@ -62,7 +65,7 @@ class SourceReferenceFormatter
6265
return errorOutput.str();
6366
}
6467

65-
private:
68+
protected:
6669
/// Prints source name if location is given.
6770
void printSourceName(SourceReference const& _ref);
6871

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
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+
/**
18+
* Formatting functions for errors referencing positions and locations in the source.
19+
*/
20+
21+
#include <liblangutil/SourceReferenceFormatterHuman.h>
22+
#include <liblangutil/Scanner.h>
23+
#include <liblangutil/Exceptions.h>
24+
#include <cmath>
25+
#include <iomanip>
26+
27+
using namespace std;
28+
using namespace dev;
29+
using namespace dev::formatting;
30+
using namespace langutil;
31+
32+
AnsiColorized SourceReferenceFormatterHuman::normalColored() const
33+
{
34+
return AnsiColorized(m_stream, m_colored, {WHITE});
35+
}
36+
37+
AnsiColorized SourceReferenceFormatterHuman::frameColored() const
38+
{
39+
return AnsiColorized(m_stream, m_colored, {BOLD, BLUE});
40+
}
41+
42+
AnsiColorized SourceReferenceFormatterHuman::errorColored() const
43+
{
44+
return AnsiColorized(m_stream, m_colored, {BOLD, RED});
45+
}
46+
47+
AnsiColorized SourceReferenceFormatterHuman::messageColored() const
48+
{
49+
return AnsiColorized(m_stream, m_colored, {BOLD, WHITE});
50+
}
51+
52+
AnsiColorized SourceReferenceFormatterHuman::secondaryColored() const
53+
{
54+
return AnsiColorized(m_stream, m_colored, {BOLD, CYAN});
55+
}
56+
57+
AnsiColorized SourceReferenceFormatterHuman::highlightColored() const
58+
{
59+
return AnsiColorized(m_stream, m_colored, {YELLOW});
60+
}
61+
62+
AnsiColorized SourceReferenceFormatterHuman::diagColored() const
63+
{
64+
return AnsiColorized(m_stream, m_colored, {BOLD, YELLOW});
65+
}
66+
67+
void SourceReferenceFormatterHuman::printSourceLocation(SourceReference const& _ref)
68+
{
69+
if (_ref.position.line < 0)
70+
return; // Nothing we can print here
71+
72+
int const leftpad = static_cast<int>(log10(max(_ref.position.line, 1))) + 1;
73+
74+
// line 0: source name
75+
frameColored() << string(leftpad, ' ') << "--> ";
76+
m_stream << _ref.sourceName << ":" << (_ref.position.line + 1) << ":" << (_ref.position.column + 1) << ": " << '\n';
77+
78+
if (!_ref.multiline)
79+
{
80+
int const locationLength = _ref.endColumn - _ref.startColumn;
81+
82+
// line 1:
83+
m_stream << string(leftpad, ' ');
84+
frameColored() << " |" << '\n';
85+
86+
// line 2:
87+
frameColored() << (_ref.position.line + 1) << " | ";
88+
m_stream << _ref.text.substr(0, _ref.startColumn);
89+
highlightColored() << _ref.text.substr(_ref.startColumn, locationLength);
90+
m_stream << _ref.text.substr(_ref.endColumn) << '\n';
91+
92+
// line 3:
93+
m_stream << string(leftpad, ' ');
94+
frameColored() << " | ";
95+
for_each(
96+
_ref.text.cbegin(),
97+
_ref.text.cbegin() + _ref.startColumn,
98+
[this](char ch) { m_stream << (ch == '\t' ? '\t' : ' '); }
99+
);
100+
diagColored() << string(locationLength, '^') << '\n';
101+
}
102+
else
103+
{
104+
// line 1:
105+
m_stream << string(leftpad, ' ');
106+
frameColored() << " |" << '\n';
107+
108+
// line 2:
109+
frameColored() << (_ref.position.line + 1) << " | ";
110+
m_stream << _ref.text.substr(0, _ref.startColumn);
111+
highlightColored() << _ref.text.substr(_ref.startColumn) << '\n';
112+
113+
// line 3:
114+
frameColored() << string(leftpad, ' ') << " | ";
115+
m_stream << string(_ref.startColumn, ' ');
116+
diagColored() << "^ (Relevant source part starts here and spans across multiple lines).\n";
117+
}
118+
}
119+
120+
void SourceReferenceFormatterHuman::printExceptionInformation(SourceReferenceExtractor::Message const& _msg)
121+
{
122+
// exception header line
123+
errorColored() << _msg.category;
124+
messageColored() << ": " << _msg.primary.message << '\n';
125+
126+
printSourceLocation(_msg.primary);
127+
128+
for (auto const& secondary: _msg.secondary)
129+
{
130+
secondaryColored() << "Note";
131+
messageColored() << ": " << secondary.message << '\n';
132+
printSourceLocation(secondary);
133+
}
134+
135+
m_stream << '\n';
136+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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+
/**
18+
* Formatting functions for errors referencing positions and locations in the source.
19+
*/
20+
21+
#pragma once
22+
23+
#include <liblangutil/SourceReferenceExtractor.h>
24+
#include <liblangutil/SourceReferenceFormatter.h> // SourceReferenceFormatterBase
25+
26+
#include <libdevcore/AnsiColorized.h>
27+
28+
#include <ostream>
29+
#include <sstream>
30+
#include <functional>
31+
32+
namespace dev
33+
{
34+
struct Exception; // forward
35+
}
36+
37+
namespace langutil
38+
{
39+
40+
struct SourceLocation;
41+
struct SourceReference;
42+
43+
class SourceReferenceFormatterHuman: public SourceReferenceFormatter
44+
{
45+
public:
46+
SourceReferenceFormatterHuman(std::ostream& _stream, bool colored):
47+
SourceReferenceFormatter{_stream}, m_colored{colored}
48+
{}
49+
50+
void printSourceLocation(SourceReference const& _ref) override;
51+
void printExceptionInformation(SourceReferenceExtractor::Message const& _msg) override;
52+
using SourceReferenceFormatter::printExceptionInformation;
53+
54+
static std::string formatExceptionInformation(
55+
dev::Exception const& _exception,
56+
std::string const& _name,
57+
bool colored = false
58+
)
59+
{
60+
std::ostringstream errorOutput;
61+
62+
SourceReferenceFormatterHuman formatter(errorOutput, colored);
63+
formatter.printExceptionInformation(_exception, _name);
64+
return errorOutput.str();
65+
}
66+
67+
private:
68+
dev::AnsiColorized normalColored() const;
69+
dev::AnsiColorized frameColored() const;
70+
dev::AnsiColorized errorColored() const;
71+
dev::AnsiColorized messageColored() const;
72+
dev::AnsiColorized secondaryColored() const;
73+
dev::AnsiColorized highlightColored() const;
74+
dev::AnsiColorized diagColored() const;
75+
76+
private:
77+
bool m_colored;
78+
};
79+
80+
}

solc/CommandLineInterface.cpp

+34-6
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <libsolidity/interface/CompilerStack.h>
3636
#include <libsolidity/interface/StandardCompiler.h>
3737
#include <liblangutil/SourceReferenceFormatter.h>
38+
#include <liblangutil/SourceReferenceFormatterHuman.h>
3839
#include <libsolidity/interface/GasEstimator.h>
3940
#include <libsolidity/interface/AssemblyStack.h>
4041

@@ -46,6 +47,8 @@
4647
#include <libdevcore/CommonIO.h>
4748
#include <libdevcore/JSON.h>
4849

50+
#include <memory>
51+
4952
#include <boost/filesystem.hpp>
5053
#include <boost/filesystem/operations.hpp>
5154
#include <boost/algorithm/string.hpp>
@@ -134,6 +137,9 @@ static string const g_strStrictAssembly = "strict-assembly";
134137
static string const g_strPrettyJson = "pretty-json";
135138
static string const g_strVersion = "version";
136139
static string const g_strIgnoreMissingFiles = "ignore-missing";
140+
static string const g_strColor = "color";
141+
static string const g_strNoColor = "no-color";
142+
static string const g_strNewReporter = "new-reporter";
137143

138144
static string const g_argAbi = g_strAbi;
139145
static string const g_argPrettyJson = g_strPrettyJson;
@@ -169,6 +175,9 @@ static string const g_argStrictAssembly = g_strStrictAssembly;
169175
static string const g_argVersion = g_strVersion;
170176
static string const g_stdinFileName = g_stdinFileNameStr;
171177
static string const g_argIgnoreMissingFiles = g_strIgnoreMissingFiles;
178+
static string const g_argColor = g_strColor;
179+
static string const g_argNoColor = g_strNoColor;
180+
static string const g_argNewReporter = g_strNewReporter;
172181

173182
/// Possible arguments to for --combined-json
174183
static set<string> const g_combinedJsonArgs
@@ -652,6 +661,9 @@ Allowed options)",
652661
po::value<string>()->value_name("path(s)"),
653662
"Allow a given path for imports. A list of paths can be supplied by separating them with a comma."
654663
)
664+
(g_argColor.c_str(), "Force colored output.")
665+
(g_argNoColor.c_str(), "Explicitly disable colored output, disabling terminal auto-detection.")
666+
(g_argNewReporter.c_str(), "Enables new diagnostics reporter.")
655667
(g_argIgnoreMissingFiles.c_str(), "Ignore missing files.");
656668
po::options_description outputComponents("Output Components");
657669
outputComponents.add_options()
@@ -691,6 +703,14 @@ Allowed options)",
691703
return false;
692704
}
693705

706+
if (m_args.count(g_argColor) && m_args.count(g_argNoColor))
707+
{
708+
serr() << "Option " << g_argColor << " and " << g_argNoColor << " are mutualy exclusive." << endl;
709+
return false;
710+
}
711+
712+
m_coloredOutput = !m_args.count(g_argNoColor) && (isatty(STDERR_FILENO) || m_args.count(g_argColor));
713+
694714
if (m_args.count(g_argHelp) || (isatty(fileno(stdin)) && _argc == 1))
695715
{
696716
sout() << desc;
@@ -858,7 +878,11 @@ bool CommandLineInterface::processInput()
858878

859879
m_compiler.reset(new CompilerStack(fileReader));
860880

861-
SourceReferenceFormatter formatter(serr(false));
881+
unique_ptr<SourceReferenceFormatter> formatter;
882+
if (m_args.count(g_argNewReporter))
883+
formatter = make_unique<SourceReferenceFormatterHuman>(serr(false), m_coloredOutput);
884+
else
885+
formatter = make_unique<SourceReferenceFormatter>(serr(false));
862886

863887
try
864888
{
@@ -881,7 +905,7 @@ bool CommandLineInterface::processInput()
881905
for (auto const& error: m_compiler->errors())
882906
{
883907
g_hasOutput = true;
884-
formatter.printExceptionInformation(
908+
formatter->printExceptionInformation(
885909
*error,
886910
(error->type() == Error::Type::Warning) ? "Warning" : "Error"
887911
);
@@ -893,7 +917,7 @@ bool CommandLineInterface::processInput()
893917
catch (CompilerError const& _exception)
894918
{
895919
g_hasOutput = true;
896-
formatter.printExceptionInformation(_exception, "Compiler error");
920+
formatter->printExceptionInformation(_exception, "Compiler error");
897921
return false;
898922
}
899923
catch (InternalCompilerError const& _exception)
@@ -915,7 +939,7 @@ bool CommandLineInterface::processInput()
915939
else
916940
{
917941
g_hasOutput = true;
918-
formatter.printExceptionInformation(_error, _error.typeName());
942+
formatter->printExceptionInformation(_error, _error.typeName());
919943
}
920944

921945
return false;
@@ -1221,12 +1245,16 @@ bool CommandLineInterface::assemble(
12211245
for (auto const& sourceAndStack: assemblyStacks)
12221246
{
12231247
auto const& stack = sourceAndStack.second;
1224-
SourceReferenceFormatter formatter(serr(false));
1248+
unique_ptr<SourceReferenceFormatter> formatter;
1249+
if (m_args.count(g_argNewReporter))
1250+
formatter = make_unique<SourceReferenceFormatterHuman>(serr(false), m_coloredOutput);
1251+
else
1252+
formatter = make_unique<SourceReferenceFormatter>(serr(false));
12251253

12261254
for (auto const& error: stack.errors())
12271255
{
12281256
g_hasOutput = true;
1229-
formatter.printExceptionInformation(
1257+
formatter->printExceptionInformation(
12301258
*error,
12311259
(error->type() == Error::Type::Warning) ? "Warning" : "Error"
12321260
);

solc/CommandLineInterface.h

+2
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ class CommandLineInterface
109109
std::unique_ptr<dev::solidity::CompilerStack> m_compiler;
110110
/// EVM version to use
111111
EVMVersion m_evmVersion;
112+
/// Whether or not to colorize diagnostics output.
113+
bool m_coloredOutput = true;
112114
};
113115

114116
}

0 commit comments

Comments
 (0)