Merge pull request #5511 from ethereum/cp-error-output

[solc] colorized diagnostics output
This commit is contained in:
chriseth 2019-02-07 13:20:07 +01:00 committed by GitHub
commit 8992024302
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 356 additions and 12 deletions

View File

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

View File

@ -0,0 +1,91 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <ostream>
#include <vector>
namespace dev
{
namespace formatting
{
// control codes
static constexpr char const* RESET = "\033[0m";
static constexpr char const* INVERSE = "\033[7m";
static constexpr char const* BOLD = "\033[1m";
static constexpr char const* BRIGHT = BOLD;
// standard foreground colors
static constexpr char const* BLACK = "\033[30m";
static constexpr char const* RED = "\033[31m";
static constexpr char const* GREEN = "\033[32m";
static constexpr char const* YELLOW = "\033[33m";
static constexpr char const* BLUE = "\033[34m";
static constexpr char const* MAGENTA = "\033[35m";
static constexpr char const* CYAN = "\033[36m";
static constexpr char const* WHITE = "\033[37m";
// standard background colors
static constexpr char const* BLACK_BACKGROUND = "\033[40m";
static constexpr char const* RED_BACKGROUND = "\033[41m";
static constexpr char const* GREEN_BACKGROUND = "\033[42m";
static constexpr char const* YELLOW_BACKGROUND = "\033[43m";
static constexpr char const* BLUE_BACKGROUND = "\033[44m";
static constexpr char const* MAGENTA_BACKGROUND = "\033[45m";
static constexpr char const* CYAN_BACKGROUND = "\033[46m";
static constexpr char const* WHITE_BACKGROUND = "\033[47m";
// 256-bit-colors (incomplete set)
static constexpr char const* RED_BACKGROUND_256 = "\033[48;5;160m";
static constexpr char const* ORANGE_BACKGROUND_256 = "\033[48;5;166m";
}
/// AnsiColorized provides a convenience helper to colorize ostream with formatting-reset assured.
class AnsiColorized
{
public:
AnsiColorized(std::ostream& _os, bool const _enabled, std::vector<char const*>&& _formatting):
m_stream{_os}, m_enabled{_enabled}, m_codes{std::move(_formatting)}
{
if (m_enabled)
for (auto const& code: m_codes)
m_stream << code;
}
~AnsiColorized()
{
if (m_enabled)
m_stream << formatting::RESET;
}
template <typename T>
std::ostream& operator<<(T&& _t)
{
return m_stream << std::forward<T>(_t);
}
private:
std::ostream& m_stream;
bool m_enabled;
std::vector<char const*> m_codes;
};
}

View File

@ -1,5 +1,6 @@
set(sources set(sources
Algorithms.h Algorithms.h
AnsiColorized.h
Assertions.h Assertions.h
Common.h Common.h
CommonData.cpp CommonData.cpp

View File

@ -16,6 +16,8 @@ set(sources
SourceReferenceExtractor.h SourceReferenceExtractor.h
SourceReferenceFormatter.cpp SourceReferenceFormatter.cpp
SourceReferenceFormatter.h SourceReferenceFormatter.h
SourceReferenceFormatterHuman.cpp
SourceReferenceFormatterHuman.h
Token.cpp Token.cpp
Token.h Token.h
UndefMacros.h UndefMacros.h

View File

@ -44,11 +44,14 @@ public:
m_stream(_stream) m_stream(_stream)
{} {}
virtual ~SourceReferenceFormatter() = default;
/// Prints source location if it is given. /// Prints source location if it is given.
void printSourceLocation(SourceLocation const* _location); virtual void printSourceLocation(SourceReference const& _ref);
void printSourceLocation(SourceReference const& _ref); virtual void printExceptionInformation(SourceReferenceExtractor::Message const& _msg);
void printExceptionInformation(dev::Exception const& _error, std::string const& _category);
void printExceptionInformation(SourceReferenceExtractor::Message const& _msg); virtual void printSourceLocation(SourceLocation const* _location);
virtual void printExceptionInformation(dev::Exception const& _error, std::string const& _category);
static std::string formatExceptionInformation( static std::string formatExceptionInformation(
dev::Exception const& _exception, dev::Exception const& _exception,
@ -62,7 +65,7 @@ public:
return errorOutput.str(); return errorOutput.str();
} }
private: protected:
/// Prints source name if location is given. /// Prints source name if location is given.
void printSourceName(SourceReference const& _ref); void printSourceName(SourceReference const& _ref);

View File

@ -0,0 +1,136 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Formatting functions for errors referencing positions and locations in the source.
*/
#include <liblangutil/SourceReferenceFormatterHuman.h>
#include <liblangutil/Scanner.h>
#include <liblangutil/Exceptions.h>
#include <cmath>
#include <iomanip>
using namespace std;
using namespace dev;
using namespace dev::formatting;
using namespace langutil;
AnsiColorized SourceReferenceFormatterHuman::normalColored() const
{
return AnsiColorized(m_stream, m_colored, {WHITE});
}
AnsiColorized SourceReferenceFormatterHuman::frameColored() const
{
return AnsiColorized(m_stream, m_colored, {BOLD, BLUE});
}
AnsiColorized SourceReferenceFormatterHuman::errorColored() const
{
return AnsiColorized(m_stream, m_colored, {BOLD, RED});
}
AnsiColorized SourceReferenceFormatterHuman::messageColored() const
{
return AnsiColorized(m_stream, m_colored, {BOLD, WHITE});
}
AnsiColorized SourceReferenceFormatterHuman::secondaryColored() const
{
return AnsiColorized(m_stream, m_colored, {BOLD, CYAN});
}
AnsiColorized SourceReferenceFormatterHuman::highlightColored() const
{
return AnsiColorized(m_stream, m_colored, {YELLOW});
}
AnsiColorized SourceReferenceFormatterHuman::diagColored() const
{
return AnsiColorized(m_stream, m_colored, {BOLD, YELLOW});
}
void SourceReferenceFormatterHuman::printSourceLocation(SourceReference const& _ref)
{
if (_ref.position.line < 0)
return; // Nothing we can print here
int const leftpad = static_cast<int>(log10(max(_ref.position.line, 1))) + 1;
// line 0: source name
frameColored() << string(leftpad, ' ') << "--> ";
m_stream << _ref.sourceName << ":" << (_ref.position.line + 1) << ":" << (_ref.position.column + 1) << ": " << '\n';
if (!_ref.multiline)
{
int const locationLength = _ref.endColumn - _ref.startColumn;
// line 1:
m_stream << string(leftpad, ' ');
frameColored() << " |" << '\n';
// line 2:
frameColored() << (_ref.position.line + 1) << " | ";
m_stream << _ref.text.substr(0, _ref.startColumn);
highlightColored() << _ref.text.substr(_ref.startColumn, locationLength);
m_stream << _ref.text.substr(_ref.endColumn) << '\n';
// line 3:
m_stream << string(leftpad, ' ');
frameColored() << " | ";
for_each(
_ref.text.cbegin(),
_ref.text.cbegin() + _ref.startColumn,
[this](char ch) { m_stream << (ch == '\t' ? '\t' : ' '); }
);
diagColored() << string(locationLength, '^') << '\n';
}
else
{
// line 1:
m_stream << string(leftpad, ' ');
frameColored() << " |" << '\n';
// line 2:
frameColored() << (_ref.position.line + 1) << " | ";
m_stream << _ref.text.substr(0, _ref.startColumn);
highlightColored() << _ref.text.substr(_ref.startColumn) << '\n';
// line 3:
frameColored() << string(leftpad, ' ') << " | ";
m_stream << string(_ref.startColumn, ' ');
diagColored() << "^ (Relevant source part starts here and spans across multiple lines).\n";
}
}
void SourceReferenceFormatterHuman::printExceptionInformation(SourceReferenceExtractor::Message const& _msg)
{
// exception header line
errorColored() << _msg.category;
messageColored() << ": " << _msg.primary.message << '\n';
printSourceLocation(_msg.primary);
for (auto const& secondary: _msg.secondary)
{
secondaryColored() << "Note";
messageColored() << ": " << secondary.message << '\n';
printSourceLocation(secondary);
}
m_stream << '\n';
}

View File

@ -0,0 +1,80 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Formatting functions for errors referencing positions and locations in the source.
*/
#pragma once
#include <liblangutil/SourceReferenceExtractor.h>
#include <liblangutil/SourceReferenceFormatter.h> // SourceReferenceFormatterBase
#include <libdevcore/AnsiColorized.h>
#include <ostream>
#include <sstream>
#include <functional>
namespace dev
{
struct Exception; // forward
}
namespace langutil
{
struct SourceLocation;
struct SourceReference;
class SourceReferenceFormatterHuman: public SourceReferenceFormatter
{
public:
SourceReferenceFormatterHuman(std::ostream& _stream, bool colored):
SourceReferenceFormatter{_stream}, m_colored{colored}
{}
void printSourceLocation(SourceReference const& _ref) override;
void printExceptionInformation(SourceReferenceExtractor::Message const& _msg) override;
using SourceReferenceFormatter::printExceptionInformation;
static std::string formatExceptionInformation(
dev::Exception const& _exception,
std::string const& _name,
bool colored = false
)
{
std::ostringstream errorOutput;
SourceReferenceFormatterHuman formatter(errorOutput, colored);
formatter.printExceptionInformation(_exception, _name);
return errorOutput.str();
}
private:
dev::AnsiColorized normalColored() const;
dev::AnsiColorized frameColored() const;
dev::AnsiColorized errorColored() const;
dev::AnsiColorized messageColored() const;
dev::AnsiColorized secondaryColored() const;
dev::AnsiColorized highlightColored() const;
dev::AnsiColorized diagColored() const;
private:
bool m_colored;
};
}

View File

@ -35,6 +35,7 @@
#include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/CompilerStack.h>
#include <libsolidity/interface/StandardCompiler.h> #include <libsolidity/interface/StandardCompiler.h>
#include <liblangutil/SourceReferenceFormatter.h> #include <liblangutil/SourceReferenceFormatter.h>
#include <liblangutil/SourceReferenceFormatterHuman.h>
#include <libsolidity/interface/GasEstimator.h> #include <libsolidity/interface/GasEstimator.h>
#include <libsolidity/interface/AssemblyStack.h> #include <libsolidity/interface/AssemblyStack.h>
@ -46,6 +47,8 @@
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/JSON.h> #include <libdevcore/JSON.h>
#include <memory>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
@ -134,6 +137,9 @@ static string const g_strStrictAssembly = "strict-assembly";
static string const g_strPrettyJson = "pretty-json"; static string const g_strPrettyJson = "pretty-json";
static string const g_strVersion = "version"; static string const g_strVersion = "version";
static string const g_strIgnoreMissingFiles = "ignore-missing"; static string const g_strIgnoreMissingFiles = "ignore-missing";
static string const g_strColor = "color";
static string const g_strNoColor = "no-color";
static string const g_strNewReporter = "new-reporter";
static string const g_argAbi = g_strAbi; static string const g_argAbi = g_strAbi;
static string const g_argPrettyJson = g_strPrettyJson; static string const g_argPrettyJson = g_strPrettyJson;
@ -169,6 +175,9 @@ static string const g_argStrictAssembly = g_strStrictAssembly;
static string const g_argVersion = g_strVersion; static string const g_argVersion = g_strVersion;
static string const g_stdinFileName = g_stdinFileNameStr; static string const g_stdinFileName = g_stdinFileNameStr;
static string const g_argIgnoreMissingFiles = g_strIgnoreMissingFiles; static string const g_argIgnoreMissingFiles = g_strIgnoreMissingFiles;
static string const g_argColor = g_strColor;
static string const g_argNoColor = g_strNoColor;
static string const g_argNewReporter = g_strNewReporter;
/// Possible arguments to for --combined-json /// Possible arguments to for --combined-json
static set<string> const g_combinedJsonArgs static set<string> const g_combinedJsonArgs
@ -652,6 +661,9 @@ Allowed options)",
po::value<string>()->value_name("path(s)"), po::value<string>()->value_name("path(s)"),
"Allow a given path for imports. A list of paths can be supplied by separating them with a comma." "Allow a given path for imports. A list of paths can be supplied by separating them with a comma."
) )
(g_argColor.c_str(), "Force colored output.")
(g_argNoColor.c_str(), "Explicitly disable colored output, disabling terminal auto-detection.")
(g_argNewReporter.c_str(), "Enables new diagnostics reporter.")
(g_argIgnoreMissingFiles.c_str(), "Ignore missing files."); (g_argIgnoreMissingFiles.c_str(), "Ignore missing files.");
po::options_description outputComponents("Output Components"); po::options_description outputComponents("Output Components");
outputComponents.add_options() outputComponents.add_options()
@ -691,6 +703,14 @@ Allowed options)",
return false; return false;
} }
if (m_args.count(g_argColor) && m_args.count(g_argNoColor))
{
serr() << "Option " << g_argColor << " and " << g_argNoColor << " are mutualy exclusive." << endl;
return false;
}
m_coloredOutput = !m_args.count(g_argNoColor) && (isatty(STDERR_FILENO) || m_args.count(g_argColor));
if (m_args.count(g_argHelp) || (isatty(fileno(stdin)) && _argc == 1)) if (m_args.count(g_argHelp) || (isatty(fileno(stdin)) && _argc == 1))
{ {
sout() << desc; sout() << desc;
@ -858,7 +878,11 @@ bool CommandLineInterface::processInput()
m_compiler.reset(new CompilerStack(fileReader)); m_compiler.reset(new CompilerStack(fileReader));
SourceReferenceFormatter formatter(serr(false)); unique_ptr<SourceReferenceFormatter> formatter;
if (m_args.count(g_argNewReporter))
formatter = make_unique<SourceReferenceFormatterHuman>(serr(false), m_coloredOutput);
else
formatter = make_unique<SourceReferenceFormatter>(serr(false));
try try
{ {
@ -881,7 +905,7 @@ bool CommandLineInterface::processInput()
for (auto const& error: m_compiler->errors()) for (auto const& error: m_compiler->errors())
{ {
g_hasOutput = true; g_hasOutput = true;
formatter.printExceptionInformation( formatter->printExceptionInformation(
*error, *error,
(error->type() == Error::Type::Warning) ? "Warning" : "Error" (error->type() == Error::Type::Warning) ? "Warning" : "Error"
); );
@ -893,7 +917,7 @@ bool CommandLineInterface::processInput()
catch (CompilerError const& _exception) catch (CompilerError const& _exception)
{ {
g_hasOutput = true; g_hasOutput = true;
formatter.printExceptionInformation(_exception, "Compiler error"); formatter->printExceptionInformation(_exception, "Compiler error");
return false; return false;
} }
catch (InternalCompilerError const& _exception) catch (InternalCompilerError const& _exception)
@ -915,7 +939,7 @@ bool CommandLineInterface::processInput()
else else
{ {
g_hasOutput = true; g_hasOutput = true;
formatter.printExceptionInformation(_error, _error.typeName()); formatter->printExceptionInformation(_error, _error.typeName());
} }
return false; return false;
@ -1221,12 +1245,16 @@ bool CommandLineInterface::assemble(
for (auto const& sourceAndStack: assemblyStacks) for (auto const& sourceAndStack: assemblyStacks)
{ {
auto const& stack = sourceAndStack.second; auto const& stack = sourceAndStack.second;
SourceReferenceFormatter formatter(serr(false)); unique_ptr<SourceReferenceFormatter> formatter;
if (m_args.count(g_argNewReporter))
formatter = make_unique<SourceReferenceFormatterHuman>(serr(false), m_coloredOutput);
else
formatter = make_unique<SourceReferenceFormatter>(serr(false));
for (auto const& error: stack.errors()) for (auto const& error: stack.errors())
{ {
g_hasOutput = true; g_hasOutput = true;
formatter.printExceptionInformation( formatter->printExceptionInformation(
*error, *error,
(error->type() == Error::Type::Warning) ? "Warning" : "Error" (error->type() == Error::Type::Warning) ? "Warning" : "Error"
); );

View File

@ -109,6 +109,8 @@ private:
std::unique_ptr<dev::solidity::CompilerStack> m_compiler; std::unique_ptr<dev::solidity::CompilerStack> m_compiler;
/// EVM version to use /// EVM version to use
EVMVersion m_evmVersion; EVMVersion m_evmVersion;
/// Whether or not to colorize diagnostics output.
bool m_coloredOutput = true;
}; };
} }