Remove CharStream from SourceLocation.

This commit is contained in:
chriseth 2021-06-29 14:38:59 +02:00
parent 57d32ca252
commit f75b55071e
73 changed files with 613 additions and 560 deletions

View File

@ -76,15 +76,15 @@ namespace
string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location)
{ {
if (!_location.hasText() || _sourceCodes.empty()) if (!_location.hasText() || _sourceCodes.empty())
return ""; return {};
auto it = _sourceCodes.find(_location.source->name()); auto it = _sourceCodes.find(*_location.sourceName);
if (it == _sourceCodes.end()) if (it == _sourceCodes.end())
return ""; return {};
string const& source = it->second; string const& source = it->second;
if (static_cast<size_t>(_location.start) >= source.size()) if (static_cast<size_t>(_location.start) >= source.size())
return ""; return {};
string cut = source.substr(static_cast<size_t>(_location.start), static_cast<size_t>(_location.end - _location.start)); string cut = source.substr(static_cast<size_t>(_location.start), static_cast<size_t>(_location.end - _location.start));
auto newLinePos = cut.find_first_of("\n"); auto newLinePos = cut.find_first_of("\n");
@ -152,8 +152,8 @@ public:
if (!m_location.isValid()) if (!m_location.isValid())
return; return;
m_out << m_prefix << " /*"; m_out << m_prefix << " /*";
if (m_location.source) if (m_location.sourceName)
m_out << " \"" + m_location.source->name() + "\""; m_out << " " + escapeAndQuoteYulString(*m_location.sourceName);
if (m_location.hasText()) if (m_location.hasText())
m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end); m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end);
m_out << " " << locationFromSources(m_sourceCodes, m_location); m_out << " " << locationFromSources(m_sourceCodes, m_location);
@ -235,9 +235,9 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices)
for (AssemblyItem const& i: m_items) for (AssemblyItem const& i: m_items)
{ {
int sourceIndex = -1; int sourceIndex = -1;
if (i.location().source) if (i.location().sourceName)
{ {
auto iter = _sourceIndices.find(i.location().source->name()); auto iter = _sourceIndices.find(*i.location().sourceName);
if (iter != _sourceIndices.end()) if (iter != _sourceIndices.end())
sourceIndex = static_cast<int>(iter->second); sourceIndex = static_cast<int>(iter->second);
} }

View File

@ -350,8 +350,8 @@ std::string AssemblyItem::computeSourceMapping(
SourceLocation const& location = item.location(); SourceLocation const& location = item.location();
int length = location.start != -1 && location.end != -1 ? location.end - location.start : -1; int length = location.start != -1 && location.end != -1 ? location.end - location.start : -1;
int sourceIndex = int sourceIndex =
location.source && _sourceIndicesMap.count(location.source->name()) ? (location.sourceName && _sourceIndicesMap.count(*location.sourceName)) ?
static_cast<int>(_sourceIndicesMap.at(location.source->name())) : static_cast<int>(_sourceIndicesMap.at(*location.sourceName)) :
-1; -1;
char jump = '-'; char jump = '-';
if (item.getJumpType() == evmasm::AssemblyItem::JumpType::IntoFunction) if (item.getJumpType() == evmasm::AssemblyItem::JumpType::IntoFunction)

View File

@ -13,6 +13,7 @@ set(sources
ParserBase.h ParserBase.h
Scanner.cpp Scanner.cpp
Scanner.h Scanner.h
CharStreamProvider.h
SemVerHandler.cpp SemVerHandler.cpp
SemVerHandler.h SemVerHandler.h
SourceLocation.h SourceLocation.h

View File

@ -45,9 +45,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** /**
* @author Christian <c@ethdev.com> * Character stream / input file.
* @date 2014
* Solidity scanner.
*/ */
#include <liblangutil/CharStream.h> #include <liblangutil/CharStream.h>
@ -118,3 +116,15 @@ tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const
} }
return tuple<int, int>(lineNumber, searchPosition - lineStart); return tuple<int, int>(lineNumber, searchPosition - lineStart);
} }
string_view CharStream::text(SourceLocation const& _location) const
{
if (!_location.hasText())
return {};
solAssert(_location.sourceName && *_location.sourceName == m_name, "");
solAssert(static_cast<size_t>(_location.end) <= m_source.size(), "");
return string_view{m_source}.substr(
static_cast<size_t>(_location.start),
static_cast<size_t>(_location.end - _location.start)
);
}

View File

@ -45,9 +45,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** /**
* @author Christian <c@ethdev.com> * Character stream / input file.
* @date 2014
* Solidity scanner.
*/ */
#pragma once #pragma once
@ -60,6 +58,8 @@
namespace solidity::langutil namespace solidity::langutil
{ {
struct SourceLocation;
/** /**
* Bidirectional stream of characters. * Bidirectional stream of characters.
* *
@ -69,8 +69,8 @@ class CharStream
{ {
public: public:
CharStream() = default; CharStream() = default;
explicit CharStream(std::string _source, std::string name): CharStream(std::string _source, std::string _name):
m_source(std::move(_source)), m_name(std::move(name)) {} m_source(std::move(_source)), m_name(std::move(_name)) {}
size_t position() const { return m_position; } size_t position() const { return m_position; }
bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); } bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); }
@ -90,6 +90,8 @@ public:
std::string const& source() const noexcept { return m_source; } std::string const& source() const noexcept { return m_source; }
std::string const& name() const noexcept { return m_name; } std::string const& name() const noexcept { return m_name; }
size_t size() const { return m_source.size(); }
///@{ ///@{
///@name Error printing helper functions ///@name Error printing helper functions
/// Functions that help pretty-printing parse errors /// Functions that help pretty-printing parse errors
@ -112,6 +114,10 @@ public:
return true; return true;
} }
/// @returns the substring of the source that the source location references.
/// Returns an empty string view if the source location does not `hasText()`.
std::string_view text(SourceLocation const& _location) const;
private: private:
std::string m_source; std::string m_source;
std::string m_name; std::string m_name;

View File

@ -0,0 +1,57 @@
/*
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/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Interface to retrieve the character stream by a source name.
*/
#pragma once
#include <liblangutil/CharStream.h>
#include <liblangutil/Exceptions.h>
#include <string>
namespace solidity::langutil
{
/**
* Interface to retrieve a CharStream (source) from a source name.
* Used especially for printing error information.
*/
class CharStreamProvider
{
public:
virtual ~CharStreamProvider() = default;
virtual CharStream const& charStream(std::string const& _sourceName) const = 0;
};
class SingletonCharStreamProvider: public CharStreamProvider
{
public:
explicit SingletonCharStreamProvider(CharStream const& _charStream):
m_charStream(_charStream) {}
CharStream const& charStream(std::string const& _sourceName) const override
{
solAssert(m_charStream.name() == _sourceName, "");
return m_charStream;
}
private:
CharStream const& m_charStream;
};
}

View File

@ -138,6 +138,7 @@ private:
void Scanner::reset(CharStream _source) void Scanner::reset(CharStream _source)
{ {
m_source = make_shared<CharStream>(std::move(_source)); m_source = make_shared<CharStream>(std::move(_source));
m_sourceName = make_shared<string>(m_source->name());
reset(); reset();
} }
@ -145,6 +146,7 @@ void Scanner::reset(shared_ptr<CharStream> _source)
{ {
solAssert(_source.get() != nullptr, "You MUST provide a CharStream when resetting."); solAssert(_source.get() != nullptr, "You MUST provide a CharStream when resetting.");
m_source = std::move(_source); m_source = std::move(_source);
m_sourceName = make_shared<string>(m_source->name());
reset(); reset();
} }
@ -497,7 +499,7 @@ Token Scanner::scanSlash()
return skipSingleLineComment(); return skipSingleLineComment();
// doxygen style /// comment // doxygen style /// comment
m_skippedComments[NextNext].location.start = firstSlashPosition; m_skippedComments[NextNext].location.start = firstSlashPosition;
m_skippedComments[NextNext].location.source = m_source; m_skippedComments[NextNext].location.sourceName = m_sourceName;
m_skippedComments[NextNext].token = Token::CommentLiteral; m_skippedComments[NextNext].token = Token::CommentLiteral;
m_skippedComments[NextNext].location.end = static_cast<int>(scanSingleLineDocComment()); m_skippedComments[NextNext].location.end = static_cast<int>(scanSingleLineDocComment());
return Token::Whitespace; return Token::Whitespace;
@ -526,7 +528,7 @@ Token Scanner::scanSlash()
return skipMultiLineComment(); return skipMultiLineComment();
// we actually have a multiline documentation comment // we actually have a multiline documentation comment
m_skippedComments[NextNext].location.start = firstSlashPosition; m_skippedComments[NextNext].location.start = firstSlashPosition;
m_skippedComments[NextNext].location.source = m_source; m_skippedComments[NextNext].location.sourceName = m_sourceName;
Token comment = scanMultiLineDocComment(); Token comment = scanMultiLineDocComment();
m_skippedComments[NextNext].location.end = static_cast<int>(sourcePos()); m_skippedComments[NextNext].location.end = static_cast<int>(sourcePos());
m_skippedComments[NextNext].token = comment; m_skippedComments[NextNext].token = comment;
@ -766,7 +768,7 @@ void Scanner::scanToken()
} }
while (token == Token::Whitespace); while (token == Token::Whitespace);
m_tokens[NextNext].location.end = static_cast<int>(sourcePos()); m_tokens[NextNext].location.end = static_cast<int>(sourcePos());
m_tokens[NextNext].location.source = m_source; m_tokens[NextNext].location.sourceName = m_sourceName;
m_tokens[NextNext].token = token; m_tokens[NextNext].token = token;
m_tokens[NextNext].extendedTokenInfo = make_tuple(m, n); m_tokens[NextNext].extendedTokenInfo = make_tuple(m, n);
} }

View File

@ -177,14 +177,6 @@ public:
Token peekNextNextToken() const { return m_tokens[NextNext].token; } Token peekNextNextToken() const { return m_tokens[NextNext].token; }
///@} ///@}
///@{
///@name Error printing helper functions
/// Functions that help pretty-printing parse errors
/// Do only use in error cases, they are quite expensive.
std::string lineAtPosition(int _position) const { return m_source->lineAtPosition(_position); }
std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source->translatePositionToLineColumn(_position); }
///@}
private: private:
inline Token setError(ScannerError _error) noexcept inline Token setError(ScannerError _error) noexcept
@ -270,6 +262,7 @@ private:
TokenDesc m_tokens[3] = {}; // desc for the current, next and nextnext token TokenDesc m_tokens[3] = {}; // desc for the current, next and nextnext token
std::shared_ptr<CharStream> m_source; std::shared_ptr<CharStream> m_source;
std::shared_ptr<std::string const> m_sourceName;
ScannerKind m_kind = ScannerKind::Solidity; ScannerKind m_kind = ScannerKind::Solidity;

View File

@ -34,6 +34,7 @@ SourceLocation const parseSourceLocation(std::string const& _input, std::string
boost::algorithm::split(pos, _input, boost::is_any_of(":")); boost::algorithm::split(pos, _input, boost::is_any_of(":"));
// TODO What to do with sourceIndex?
solAssert(pos.size() == 3, "SourceLocation string must have 3 colon separated numeric fields."); solAssert(pos.size() == 3, "SourceLocation string must have 3 colon separated numeric fields.");
auto const sourceIndex = stoi(pos[Index]); auto const sourceIndex = stoi(pos[Index]);
@ -45,12 +46,7 @@ SourceLocation const parseSourceLocation(std::string const& _input, std::string
int start = stoi(pos[Start]); int start = stoi(pos[Start]);
int end = start + stoi(pos[Length]); int end = start + stoi(pos[Length]);
// ASSUMPTION: only the name of source is used from here on, the m_source of the CharStream-Object can be empty return SourceLocation{start, end, std::make_shared<std::string>(_sourceName)};
std::shared_ptr<langutil::CharStream> source;
if (sourceIndex != -1)
source = std::make_shared<langutil::CharStream>("", _sourceName);
return SourceLocation{start, end, source};
} }
} }

View File

@ -26,8 +26,6 @@
#include <libsolutil/Assertions.h> #include <libsolutil/Assertions.h>
#include <libsolutil/Exceptions.h> #include <libsolutil/Exceptions.h>
#include <liblangutil/CharStream.h>
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <string> #include <string>
@ -44,51 +42,44 @@ struct SourceLocation
{ {
bool operator==(SourceLocation const& _other) const bool operator==(SourceLocation const& _other) const
{ {
return source.get() == _other.source.get() && start == _other.start && end == _other.end; return start == _other.start && end == _other.end && equalSources(_other);
} }
bool operator!=(SourceLocation const& _other) const { return !operator==(_other); } bool operator!=(SourceLocation const& _other) const { return !operator==(_other); }
inline bool operator<(SourceLocation const& _other) const bool operator<(SourceLocation const& _other) const
{ {
if (!source|| !_other.source) if (!sourceName || !_other.sourceName)
return std::make_tuple(int(!!source), start, end) < std::make_tuple(int(!!_other.source), _other.start, _other.end); return std::make_tuple(int(!!sourceName), start, end) < std::make_tuple(int(!!_other.sourceName), _other.start, _other.end);
else else
return std::make_tuple(source->name(), start, end) < std::make_tuple(_other.source->name(), _other.start, _other.end); return std::make_tuple(*sourceName, start, end) < std::make_tuple(*_other.sourceName, _other.start, _other.end);
} }
inline bool contains(SourceLocation const& _other) const bool contains(SourceLocation const& _other) const
{ {
if (!hasText() || !_other.hasText() || source.get() != _other.source.get()) if (!hasText() || !_other.hasText() || !equalSources(_other))
return false; return false;
return start <= _other.start && _other.end <= end; return start <= _other.start && _other.end <= end;
} }
inline bool intersects(SourceLocation const& _other) const bool intersects(SourceLocation const& _other) const
{ {
if (!hasText() || !_other.hasText() || source.get() != _other.source.get()) if (!hasText() || !_other.hasText() || !equalSources(_other))
return false; return false;
return _other.start < end && start < _other.end; return _other.start < end && start < _other.end;
} }
bool isValid() const { return source || start != -1 || end != -1; } bool equalSources(SourceLocation const& _other) const
bool hasText() const
{ {
return if (!!sourceName != !!_other.sourceName)
source && return false;
0 <= start && if (sourceName && *sourceName != *_other.sourceName)
start <= end && return false;
end <= int(source->source().length()); return true;
} }
std::string text() const bool isValid() const { return sourceName || start != -1 || end != -1; }
{
assertThrow(source, SourceLocationError, "Requested text from null source."); bool hasText() const { return sourceName && 0 <= start && start <= end; }
assertThrow(0 <= start, SourceLocationError, "Invalid source location.");
assertThrow(start <= end, SourceLocationError, "Invalid source location.");
assertThrow(end <= int(source->source().length()), SourceLocationError, "Invalid source location.");
return source->source().substr(size_t(start), size_t(end - start));
}
/// @returns the smallest SourceLocation that contains both @param _a and @param _b. /// @returns the smallest SourceLocation that contains both @param _a and @param _b.
/// Assumes that @param _a and @param _b refer to the same source (exception: if the source of either one /// Assumes that @param _a and @param _b refer to the same source (exception: if the source of either one
@ -97,8 +88,8 @@ struct SourceLocation
/// @param _b, then start resp. end of the result will be -1 as well). /// @param _b, then start resp. end of the result will be -1 as well).
static SourceLocation smallestCovering(SourceLocation _a, SourceLocation const& _b) static SourceLocation smallestCovering(SourceLocation _a, SourceLocation const& _b)
{ {
if (!_a.source) if (!_a.sourceName)
_a.source = _b.source; _a.sourceName = _b.sourceName;
if (_a.start < 0) if (_a.start < 0)
_a.start = _b.start; _a.start = _b.start;
@ -112,7 +103,7 @@ struct SourceLocation
int start = -1; int start = -1;
int end = -1; int end = -1;
std::shared_ptr<CharStream> source; std::shared_ptr<std::string const> sourceName;
}; };
SourceLocation const parseSourceLocation( SourceLocation const parseSourceLocation(
@ -127,8 +118,8 @@ inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _locat
if (!_location.isValid()) if (!_location.isValid())
return _out << "NO_LOCATION_SPECIFIED"; return _out << "NO_LOCATION_SPECIFIED";
if (_location.source) if (_location.sourceName)
_out << _location.source->name(); _out << *_location.sourceName;
_out << "[" << _location.start << "," << _location.end << "]"; _out << "[" << _location.start << "," << _location.end << "]";

View File

@ -16,8 +16,9 @@
*/ */
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
#include <liblangutil/SourceReferenceExtractor.h> #include <liblangutil/SourceReferenceExtractor.h>
#include <liblangutil/CharStream.h>
#include <liblangutil/Exceptions.h> #include <liblangutil/Exceptions.h>
#include <liblangutil/CharStreamProvider.h>
#include <liblangutil/CharStream.h>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
@ -26,46 +27,57 @@ using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::langutil; using namespace solidity::langutil;
SourceReferenceExtractor::Message SourceReferenceExtractor::extract(util::Exception const& _exception, string _category) SourceReferenceExtractor::Message SourceReferenceExtractor::extract(
CharStreamProvider const& _charStreamProvider,
util::Exception const& _exception,
string _category
)
{ {
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception); SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
string const* message = boost::get_error_info<util::errinfo_comment>(_exception); string const* message = boost::get_error_info<util::errinfo_comment>(_exception);
SourceReference primary = extract(location, message ? *message : ""); SourceReference primary = extract(_charStreamProvider, location, message ? *message : "");
std::vector<SourceReference> secondary; std::vector<SourceReference> secondary;
auto secondaryLocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception); auto secondaryLocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
if (secondaryLocation && !secondaryLocation->infos.empty()) if (secondaryLocation && !secondaryLocation->infos.empty())
for (auto const& info: secondaryLocation->infos) for (auto const& info: secondaryLocation->infos)
secondary.emplace_back(extract(&info.second, info.first)); secondary.emplace_back(extract(_charStreamProvider, &info.second, info.first));
return Message{std::move(primary), _category, std::move(secondary), nullopt}; return Message{std::move(primary), _category, std::move(secondary), nullopt};
} }
SourceReferenceExtractor::Message SourceReferenceExtractor::extract(Error const& _error) SourceReferenceExtractor::Message SourceReferenceExtractor::extract(
CharStreamProvider const& _charStreamProvider,
Error const& _error
)
{ {
string category = (_error.type() == Error::Type::Warning) ? "Warning" : "Error"; string category = (_error.type() == Error::Type::Warning) ? "Warning" : "Error";
Message message = extract(_error, category); Message message = extract(_charStreamProvider, _error, category);
message.errorId = _error.errorId(); message.errorId = _error.errorId();
return message; return message;
} }
SourceReference SourceReferenceExtractor::extract(SourceLocation const* _location, std::string message) SourceReference SourceReferenceExtractor::extract(
CharStreamProvider const& _charStreamProvider,
SourceLocation const* _location,
std::string message
)
{ {
if (!_location || !_location->source.get()) // Nothing we can extract here if (!_location || !_location->sourceName) // Nothing we can extract here
return SourceReference::MessageOnly(std::move(message)); return SourceReference::MessageOnly(std::move(message));
if (!_location->hasText()) // No source text, so we can only extract the source name if (!_location->hasText()) // No source text, so we can only extract the source name
return SourceReference::MessageOnly(std::move(message), _location->source->name()); return SourceReference::MessageOnly(std::move(message), *_location->sourceName);
shared_ptr<CharStream> const& source = _location->source; CharStream const& charStream = _charStreamProvider.charStream(*_location->sourceName);
LineColumn const interest = source->translatePositionToLineColumn(_location->start); LineColumn const interest = charStream.translatePositionToLineColumn(_location->start);
LineColumn start = interest; LineColumn start = interest;
LineColumn end = source->translatePositionToLineColumn(_location->end); LineColumn end = charStream.translatePositionToLineColumn(_location->end);
bool const isMultiline = start.line != end.line; bool const isMultiline = start.line != end.line;
string line = source->lineAtPosition(_location->start); string line = charStream.lineAtPosition(_location->start);
int locationLength = int locationLength =
isMultiline ? isMultiline ?
@ -102,7 +114,7 @@ SourceReference SourceReferenceExtractor::extract(SourceLocation const* _locatio
return SourceReference{ return SourceReference{
std::move(message), std::move(message),
source->name(), *_location->sourceName,
interest, interest,
isMultiline, isMultiline,
line, line,

View File

@ -28,6 +28,8 @@
namespace solidity::langutil namespace solidity::langutil
{ {
class CharStreamProvider;
struct LineColumn struct LineColumn
{ {
int line = {-1}; int line = {-1};
@ -67,9 +69,9 @@ namespace SourceReferenceExtractor
std::optional<ErrorId> errorId; std::optional<ErrorId> errorId;
}; };
Message extract(util::Exception const& _exception, std::string _category); Message extract(CharStreamProvider const& _charStreamProvider, util::Exception const& _exception, std::string _category);
Message extract(Error const& _error); Message extract(CharStreamProvider const& _charStreamProvider, Error const& _error);
SourceReference extract(SourceLocation const* _location, std::string message = ""); SourceReference extract(CharStreamProvider const& _charStreamProvider, SourceLocation const* _location, std::string message = "");
} }
} }

View File

@ -173,10 +173,16 @@ void SourceReferenceFormatter::printExceptionInformation(SourceReferenceExtracto
void SourceReferenceFormatter::printExceptionInformation(util::Exception const& _exception, std::string const& _category) void SourceReferenceFormatter::printExceptionInformation(util::Exception const& _exception, std::string const& _category)
{ {
printExceptionInformation(SourceReferenceExtractor::extract(_exception, _category)); printExceptionInformation(SourceReferenceExtractor::extract(m_charStreamProvider, _exception, _category));
}
void SourceReferenceFormatter::printErrorInformation(ErrorList const& _errors)
{
for (auto const& error: _errors)
printErrorInformation(*error);
} }
void SourceReferenceFormatter::printErrorInformation(Error const& _error) void SourceReferenceFormatter::printErrorInformation(Error const& _error)
{ {
printExceptionInformation(SourceReferenceExtractor::extract(_error)); printExceptionInformation(SourceReferenceExtractor::extract(m_charStreamProvider, _error));
} }

View File

@ -23,6 +23,7 @@
#include <liblangutil/Exceptions.h> #include <liblangutil/Exceptions.h>
#include <liblangutil/SourceReferenceExtractor.h> #include <liblangutil/SourceReferenceExtractor.h>
#include <liblangutil/CharStreamProvider.h>
#include <libsolutil/AnsiColorized.h> #include <libsolutil/AnsiColorized.h>
@ -37,34 +38,53 @@ struct SourceLocation;
class SourceReferenceFormatter class SourceReferenceFormatter
{ {
public: public:
SourceReferenceFormatter(std::ostream& _stream, bool _colored, bool _withErrorIds): SourceReferenceFormatter(
m_stream(_stream), m_colored(_colored), m_withErrorIds(_withErrorIds) std::ostream& _stream,
CharStreamProvider const& _charStreamProvider,
bool _colored,
bool _withErrorIds
):
m_stream(_stream), m_charStreamProvider(_charStreamProvider), m_colored(_colored), m_withErrorIds(_withErrorIds)
{} {}
/// Prints source location if it is given. /// Prints source location if it is given.
void printSourceLocation(SourceReference const& _ref); void printSourceLocation(SourceReference const& _ref);
void printExceptionInformation(SourceReferenceExtractor::Message const& _msg); void printExceptionInformation(SourceReferenceExtractor::Message const& _msg);
void printExceptionInformation(util::Exception const& _exception, std::string const& _category); void printExceptionInformation(util::Exception const& _exception, std::string const& _category);
void printErrorInformation(langutil::ErrorList const& _errors);
void printErrorInformation(Error const& _error); void printErrorInformation(Error const& _error);
static std::string formatExceptionInformation( static std::string formatExceptionInformation(
util::Exception const& _exception, util::Exception const& _exception,
std::string const& _name, std::string const& _name,
CharStreamProvider const& _charStreamProvider,
bool _colored = false, bool _colored = false,
bool _withErrorIds = false bool _withErrorIds = false
) )
{ {
std::ostringstream errorOutput; std::ostringstream errorOutput;
SourceReferenceFormatter formatter(errorOutput, _colored, _withErrorIds); SourceReferenceFormatter formatter(errorOutput, _charStreamProvider, _colored, _withErrorIds);
formatter.printExceptionInformation(_exception, _name); formatter.printExceptionInformation(_exception, _name);
return errorOutput.str(); return errorOutput.str();
} }
static std::string formatErrorInformation(Error const& _error) static std::string formatErrorInformation(
Error const& _error,
CharStreamProvider const& _charStreamProvider
)
{ {
return formatExceptionInformation( return formatExceptionInformation(
_error, _error,
(_error.type() == Error::Type::Warning) ? "Warning" : "Error" (_error.type() == Error::Type::Warning) ? "Warning" : "Error",
_charStreamProvider
);
}
static std::string formatErrorInformation(Error const& _error, CharStream const& _charStream)
{
return formatErrorInformation(
_error,
SingletonCharStreamProvider(_charStream)
); );
} }
@ -79,6 +99,7 @@ private:
private: private:
std::ostream& m_stream; std::ostream& m_stream;
CharStreamProvider const& m_charStreamProvider;
bool m_colored; bool m_colored;
bool m_withErrorIds; bool m_withErrorIds;
}; };

View File

@ -520,9 +520,9 @@ bool DeclarationRegistrationHelper::registerDeclaration(
Declaration const* conflictingDeclaration = _container.conflictingDeclaration(_declaration, _name); Declaration const* conflictingDeclaration = _container.conflictingDeclaration(_declaration, _name);
solAssert(conflictingDeclaration, ""); solAssert(conflictingDeclaration, "");
bool const comparable = bool const comparable =
_errorLocation->source && _errorLocation->sourceName &&
conflictingDeclaration->location().source && conflictingDeclaration->location().sourceName &&
_errorLocation->source->name() == conflictingDeclaration->location().source->name(); *_errorLocation->sourceName == *conflictingDeclaration->location().sourceName;
if (comparable && _errorLocation->start < conflictingDeclaration->location().start) if (comparable && _errorLocation->start < conflictingDeclaration->location().start)
{ {
firstDeclarationLocation = *_errorLocation; firstDeclarationLocation = *_errorLocation;

View File

@ -68,7 +68,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit)
string(";\""); string(";\"");
// when reporting the warning, print the source name only // when reporting the warning, print the source name only
m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().source}, errorString); m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().sourceName}, errorString);
} }
if (!m_sourceUnit->annotation().useABICoderV2.set()) if (!m_sourceUnit->annotation().useABICoderV2.set())
m_sourceUnit->annotation().useABICoderV2 = true; m_sourceUnit->annotation().useABICoderV2 = true;

View File

@ -110,8 +110,8 @@ void ASTJsonConverter::setJsonNode(
optional<size_t> ASTJsonConverter::sourceIndexFromLocation(SourceLocation const& _location) const optional<size_t> ASTJsonConverter::sourceIndexFromLocation(SourceLocation const& _location) const
{ {
if (_location.source && m_sourceIndices.count(_location.source->name())) if (_location.sourceName && m_sourceIndices.count(*_location.sourceName))
return m_sourceIndices.at(_location.source->name()); return m_sourceIndices.at(*_location.sourceName);
else else
return nullopt; return nullopt;
} }

View File

@ -455,7 +455,9 @@ void CompilerContext::appendInlineAssembly(
_assembly + "\n" _assembly + "\n"
"------------------ Errors: ----------------\n"; "------------------ Errors: ----------------\n";
for (auto const& error: errorReporter.errors()) for (auto const& error: errorReporter.errors())
message += SourceReferenceFormatter::formatErrorInformation(*error); // TODO if we have "locationOverride", it will be the wrong char stream,
// but we do not have access to the solidity scanner.
message += SourceReferenceFormatter::formatErrorInformation(*error, *scanner->charStream());
message += "-------------------------------------------\n"; message += "-------------------------------------------\n";
solAssert(false, message); solAssert(false, message);

View File

@ -129,8 +129,9 @@ string IRNames::zeroValue(Type const& _type, string const& _variableName)
string sourceLocationComment(langutil::SourceLocation const& _location, IRGenerationContext const& _context) string sourceLocationComment(langutil::SourceLocation const& _location, IRGenerationContext const& _context)
{ {
solAssert(_location.sourceName, "");
return "/// @src " return "/// @src "
+ to_string(_context.sourceIndices().at(_location.source->name())) + to_string(_context.sourceIndices().at(*_location.sourceName))
+ ":" + ":"
+ to_string(_location.start) + to_string(_location.start)
+ "," + ","

View File

@ -101,7 +101,10 @@ pair<string, string> IRGenerator::run(
{ {
string errorMessage; string errorMessage;
for (auto const& error: asmStack.errors()) for (auto const& error: asmStack.errors())
errorMessage += langutil::SourceReferenceFormatter::formatErrorInformation(*error); errorMessage += langutil::SourceReferenceFormatter::formatErrorInformation(
*error,
asmStack.charStream("")
);
solAssert(false, ir + "\n\nInvalid IR generated:\n" + errorMessage + "\n"); solAssert(false, ir + "\n\nInvalid IR generated:\n" + errorMessage + "\n");
} }
asmStack.optimize(); asmStack.optimize();

View File

@ -650,7 +650,7 @@ pair<vector<smtutil::Expression>, vector<string>> BMC::modelExpressions()
if (uf->annotation().type->isValueType()) if (uf->annotation().type->isValueType())
{ {
expressionsToEvaluate.emplace_back(expr(*uf)); expressionsToEvaluate.emplace_back(expr(*uf));
expressionNames.push_back(uf->location().text()); // TODO expressionNames.push_back(uf->location().text());
} }
return {expressionsToEvaluate, expressionNames}; return {expressionsToEvaluate, expressionNames};

View File

@ -200,8 +200,9 @@ string Predicate::formatSummaryCall(vector<smtutil::Expression> const& _args) co
{ {
solAssert(isSummary(), ""); solAssert(isSummary(), "");
if (auto funCall = programFunctionCall()) //if (auto funCall = programFunctionCall())
return funCall->location().text(); // return funCall->location().text();
// TODO
/// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockChainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). /// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockChainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars).
/// Here we are interested in preInputVars to format the function call, /// Here we are interested in preInputVars to format the function call,

View File

@ -926,19 +926,6 @@ map<string, unsigned> CompilerStack::sourceIndices() const
return indices; return indices;
} }
map<unsigned, shared_ptr<CharStream>> CompilerStack::indicesToCharStreams() const
{
map<unsigned, shared_ptr<CharStream>> result;
unsigned index = 0;
for (auto const& s: m_sources)
result[index++] = s.second.scanner->charStream();
// NB: CompilerContext::yulUtilityFileName() does not have a source,
result[index++] = shared_ptr<CharStream>{};
return result;
}
Json::Value const& CompilerStack::contractABI(string const& _contractName) const Json::Value const& CompilerStack::contractABI(string const& _contractName) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisPerformed)
@ -1048,12 +1035,15 @@ string const& CompilerStack::metadata(Contract const& _contract) const
return _contract.metadata.init([&]{ return createMetadata(_contract); }); return _contract.metadata.init([&]{ return createMetadata(_contract); });
} }
Scanner const& CompilerStack::scanner(string const& _sourceName) const CharStream const& CompilerStack::charStream(string const& _sourceName) const
{ {
if (m_stackState < SourcesSet) if (m_stackState < SourcesSet)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No sources set.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No sources set."));
return *source(_sourceName).scanner; solAssert(source(_sourceName).scanner, "");
solAssert(source(_sourceName).scanner->charStream(), "");
return *source(_sourceName).scanner->charStream();
} }
SourceUnit const& CompilerStack::ast(string const& _sourceName) const SourceUnit const& CompilerStack::ast(string const& _sourceName) const
@ -1095,19 +1085,6 @@ size_t CompilerStack::functionEntryPoint(
return 0; return 0;
} }
tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocation const& _sourceLocation) const
{
int startLine;
int startColumn;
int endLine;
int endColumn;
tie(startLine, startColumn) = scanner(_sourceLocation.source->name()).translatePositionToLineColumn(_sourceLocation.start);
tie(endLine, endColumn) = scanner(_sourceLocation.source->name()).translatePositionToLineColumn(_sourceLocation.end);
return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn);
}
h256 const& CompilerStack::Source::keccak256() const h256 const& CompilerStack::Source::keccak256() const
{ {
if (keccak256HashCached == h256{}) if (keccak256HashCached == h256{})

View File

@ -38,6 +38,7 @@
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
#include <liblangutil/SourceLocation.h> #include <liblangutil/SourceLocation.h>
#include <liblangutil/CharStreamProvider.h>
#include <libevmasm/LinkerObject.h> #include <libevmasm/LinkerObject.h>
@ -57,6 +58,7 @@
namespace solidity::langutil namespace solidity::langutil
{ {
class Scanner; class Scanner;
class CharStream;
} }
@ -87,7 +89,7 @@ class DeclarationContainer;
* If error recovery is active, it is possible to progress through the stages even when * If error recovery is active, it is possible to progress through the stages even when
* there are errors. In any case, producing code is only possible without errors. * there are errors. In any case, producing code is only possible without errors.
*/ */
class CompilerStack class CompilerStack: public langutil::CharStreamProvider
{ {
public: public:
/// Noncopyable. /// Noncopyable.
@ -120,7 +122,7 @@ public:
/// and must not emit exceptions. /// and must not emit exceptions.
explicit CompilerStack(ReadCallback::Callback _readFile = ReadCallback::Callback()); explicit CompilerStack(ReadCallback::Callback _readFile = ReadCallback::Callback());
~CompilerStack(); ~CompilerStack() override;
/// @returns the list of errors that occurred during parsing and type checking. /// @returns the list of errors that occurred during parsing and type checking.
langutil::ErrorList const& errors() const { return m_errorReporter.errors(); } langutil::ErrorList const& errors() const { return m_errorReporter.errors(); }
@ -239,12 +241,8 @@ public:
/// by sourceNames(). /// by sourceNames().
std::map<std::string, unsigned> sourceIndices() const; std::map<std::string, unsigned> sourceIndices() const;
/// @returns the reverse mapping of source indices to their respective /// @returns the previously used character stream, useful for counting lines during error reporting.
/// CharStream instances. langutil::CharStream const& charStream(std::string const& _sourceName) const override;
std::map<unsigned, std::shared_ptr<langutil::CharStream>> indicesToCharStreams() const;
/// @returns the previously used scanner, useful for counting lines during error reporting.
langutil::Scanner const& scanner(std::string const& _sourceName) const;
/// @returns the parsed source unit with the supplied name. /// @returns the parsed source unit with the supplied name.
SourceUnit const& ast(std::string const& _sourceName) const; SourceUnit const& ast(std::string const& _sourceName) const;
@ -253,11 +251,6 @@ public:
/// does not exist. /// does not exist.
ContractDefinition const& contractDefinition(std::string const& _contractName) const; ContractDefinition const& contractDefinition(std::string const& _contractName) const;
/// Helper function for logs printing. Do only use in error cases, it's quite expensive.
/// line and columns are numbered starting from 1 with following order:
/// start line, start column, end line, end column
std::tuple<int, int, int, int> positionFromSourceLocation(langutil::SourceLocation const& _sourceLocation) const;
/// @returns a list of unhandled queries to the SMT solver (has to be supplied in a second run /// @returns a list of unhandled queries to the SMT solver (has to be supplied in a second run
/// by calling @a addSMTLib2Response). /// by calling @a addSMTLib2Response).
std::vector<std::string> const& unhandledSMTLib2Queries() const { return m_unhandledSMTLib2Queries; } std::vector<std::string> const& unhandledSMTLib2Queries() const { return m_unhandledSMTLib2Queries; }

View File

@ -83,9 +83,9 @@ Json::Value formatFatalError(string const& _type, string const& _message)
Json::Value formatSourceLocation(SourceLocation const* location) Json::Value formatSourceLocation(SourceLocation const* location)
{ {
Json::Value sourceLocation; Json::Value sourceLocation;
if (location && location->source) if (location && location->sourceName)
{ {
sourceLocation["file"] = location->source->name(); sourceLocation["file"] = *location->sourceName;
sourceLocation["start"] = location->start; sourceLocation["start"] = location->start;
sourceLocation["end"] = location->end; sourceLocation["end"] = location->end;
} }
@ -109,6 +109,7 @@ Json::Value formatSecondarySourceLocation(SecondarySourceLocation const* _second
} }
Json::Value formatErrorWithException( Json::Value formatErrorWithException(
CharStreamProvider const& _charStreamProvider,
util::Exception const& _exception, util::Exception const& _exception,
bool const& _warning, bool const& _warning,
string const& _type, string const& _type,
@ -119,7 +120,11 @@ Json::Value formatErrorWithException(
{ {
string message; string message;
// TODO: consider enabling color // TODO: consider enabling color
string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type); string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(
_exception,
_type,
_charStreamProvider
);
if (string const* description = boost::get_error_info<util::errinfo_comment>(_exception)) if (string const* description = boost::get_error_info<util::errinfo_comment>(_exception))
message = ((_message.length() > 0) ? (_message + ":") : "") + *description; message = ((_message.length() > 0) ? (_message + ":") : "") + *description;
@ -1017,6 +1022,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
Error const& err = dynamic_cast<Error const&>(*error); Error const& err = dynamic_cast<Error const&>(*error);
errors.append(formatErrorWithException( errors.append(formatErrorWithException(
compilerStack,
*error, *error,
err.type() == Error::Type::Warning, err.type() == Error::Type::Warning,
err.typeName(), err.typeName(),
@ -1030,6 +1036,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
catch (Error const& _error) catch (Error const& _error)
{ {
errors.append(formatErrorWithException( errors.append(formatErrorWithException(
compilerStack,
_error, _error,
false, false,
_error.typeName(), _error.typeName(),
@ -1050,6 +1057,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
catch (CompilerError const& _exception) catch (CompilerError const& _exception)
{ {
errors.append(formatErrorWithException( errors.append(formatErrorWithException(
compilerStack,
_exception, _exception,
false, false,
"CompilerError", "CompilerError",
@ -1060,6 +1068,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
catch (InternalCompilerError const& _exception) catch (InternalCompilerError const& _exception)
{ {
errors.append(formatErrorWithException( errors.append(formatErrorWithException(
compilerStack,
_exception, _exception,
false, false,
"InternalCompilerError", "InternalCompilerError",
@ -1070,6 +1079,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
catch (UnimplementedFeatureError const& _exception) catch (UnimplementedFeatureError const& _exception)
{ {
errors.append(formatErrorWithException( errors.append(formatErrorWithException(
compilerStack,
_exception, _exception,
false, false,
"UnimplementedFeatureError", "UnimplementedFeatureError",
@ -1080,6 +1090,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
catch (yul::YulException const& _exception) catch (yul::YulException const& _exception)
{ {
errors.append(formatErrorWithException( errors.append(formatErrorWithException(
compilerStack,
_exception, _exception,
false, false,
"YulException", "YulException",
@ -1090,6 +1101,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
catch (smtutil::SMTLogicError const& _exception) catch (smtutil::SMTLogicError const& _exception)
{ {
errors.append(formatErrorWithException( errors.append(formatErrorWithException(
compilerStack,
_exception, _exception,
false, false,
"SMTLogicException", "SMTLogicException",
@ -1297,6 +1309,7 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
auto err = dynamic_pointer_cast<Error const>(error); auto err = dynamic_pointer_cast<Error const>(error);
errors.append(formatErrorWithException( errors.append(formatErrorWithException(
stack,
*error, *error,
err->type() == Error::Type::Warning, err->type() == Error::Type::Warning,
err->typeName(), err->typeName(),

View File

@ -50,7 +50,12 @@ class Parser::ASTNodeFactory
{ {
public: public:
explicit ASTNodeFactory(Parser& _parser): explicit ASTNodeFactory(Parser& _parser):
m_parser(_parser), m_location{_parser.currentLocation().start, -1, _parser.currentLocation().source} {} m_parser(_parser), m_location{
_parser.currentLocation().start,
-1,
_parser.currentLocation().sourceName
}
{}
ASTNodeFactory(Parser& _parser, ASTPointer<ASTNode> const& _childNode): ASTNodeFactory(Parser& _parser, ASTPointer<ASTNode> const& _childNode):
m_parser(_parser), m_location{_childNode->location()} {} m_parser(_parser), m_location{_childNode->location()} {}
@ -63,7 +68,7 @@ public:
template <class NodeType, typename... Args> template <class NodeType, typename... Args>
ASTPointer<NodeType> createNode(Args&& ... _args) ASTPointer<NodeType> createNode(Args&& ... _args)
{ {
solAssert(m_location.source, ""); solAssert(m_location.sourceName, "");
if (m_location.end < 0) if (m_location.end < 0)
markEndPosition(); markEndPosition();
return make_shared<NodeType>(m_parser.nextID(), m_location, std::forward<Args>(_args)...); return make_shared<NodeType>(m_parser.nextID(), m_location, std::forward<Args>(_args)...);
@ -2084,7 +2089,7 @@ optional<string> Parser::findLicenseString(std::vector<ASTPointer<ASTNode>> cons
else if (matches.empty()) else if (matches.empty())
parserWarning( parserWarning(
1878_error, 1878_error,
{-1, -1, m_scanner->charStream()}, {-1, -1, m_scanner->currentLocation().sourceName},
"SPDX license identifier not provided in source file. " "SPDX license identifier not provided in source file. "
"Before publishing, consider adding a comment containing " "Before publishing, consider adding a comment containing "
"\"SPDX-License-Identifier: <SPDX-License>\" to each source file. " "\"SPDX-License-Identifier: <SPDX-License>\" to each source file. "
@ -2094,7 +2099,7 @@ optional<string> Parser::findLicenseString(std::vector<ASTPointer<ASTNode>> cons
else else
parserError( parserError(
3716_error, 3716_error,
{-1, -1, m_scanner->charStream()}, {-1, -1, m_scanner->currentLocation().sourceName},
"Multiple SPDX license identifiers found in source file. " "Multiple SPDX license identifiers found in source file. "
"Use \"AND\" or \"OR\" to combine multiple licenses. " "Use \"AND\" or \"OR\" to combine multiple licenses. "
"Please see https://spdx.org for more information." "Please see https://spdx.org for more information."

View File

@ -53,10 +53,7 @@ T AsmJsonImporter::createAsmNode(Json::Value const& _node)
{ {
T r; T r;
SourceLocation location = createSourceLocation(_node); SourceLocation location = createSourceLocation(_node);
yulAssert( yulAssert(location.hasText(), "Invalid source location in Asm AST");
location.source && 0 <= location.start && location.start <= location.end,
"Invalid source location in Asm AST"
);
r.debugData = DebugData::create(location); r.debugData = DebugData::create(location);
return r; return r;
} }

View File

@ -80,7 +80,7 @@ unique_ptr<Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner, bool _
try try
{ {
m_scanner = _scanner; m_scanner = _scanner;
if (m_charStreamMap) if (m_sourceNames)
fetchSourceLocationFromComment(); fetchSourceLocationFromComment();
auto block = make_unique<Block>(parseBlock()); auto block = make_unique<Block>(parseBlock());
if (!_reuseScanner) if (!_reuseScanner)
@ -105,7 +105,7 @@ langutil::Token Parser::advance()
void Parser::fetchSourceLocationFromComment() void Parser::fetchSourceLocationFromComment()
{ {
solAssert(m_charStreamMap.has_value(), ""); solAssert(m_sourceNames.has_value(), "");
if (m_scanner->currentCommentLiteral().empty()) if (m_scanner->currentCommentLiteral().empty())
return; return;
@ -133,13 +133,13 @@ void Parser::fetchSourceLocationFromComment()
m_errorReporter.syntaxError(6367_error, commentLocation, "Invalid value in source location mapping. Could not parse location specification."); m_errorReporter.syntaxError(6367_error, commentLocation, "Invalid value in source location mapping. Could not parse location specification.");
else if (sourceIndex == -1) else if (sourceIndex == -1)
m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, nullptr}); m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, nullptr});
else if (!(sourceIndex >= 0 && m_charStreamMap->count(static_cast<unsigned>(*sourceIndex)))) else if (!(sourceIndex >= 0 && m_sourceNames->count(static_cast<unsigned>(*sourceIndex))))
m_errorReporter.syntaxError(2674_error, commentLocation, "Invalid source mapping. Source index not defined via @use-src."); m_errorReporter.syntaxError(2674_error, commentLocation, "Invalid source mapping. Source index not defined via @use-src.");
else else
{ {
shared_ptr<CharStream> charStream = m_charStreamMap->at(static_cast<unsigned>(*sourceIndex)); shared_ptr<string const> sourceName = m_sourceNames->at(static_cast<unsigned>(*sourceIndex));
solAssert(charStream, ""); solAssert(sourceName, "");
m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, charStream}); m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, move(sourceName)});
} }
} }
} }

View File

@ -72,11 +72,11 @@ public:
explicit Parser( explicit Parser(
langutil::ErrorReporter& _errorReporter, langutil::ErrorReporter& _errorReporter,
Dialect const& _dialect, Dialect const& _dialect,
std::map<unsigned, std::shared_ptr<langutil::CharStream>> _charStreamMap std::map<unsigned, std::shared_ptr<std::string const>> _sourceNames
): ):
ParserBase(_errorReporter), ParserBase(_errorReporter),
m_dialect(_dialect), m_dialect(_dialect),
m_charStreamMap{std::move(_charStreamMap)}, m_sourceNames{std::move(_sourceNames)},
m_debugDataOverride{DebugData::create()}, m_debugDataOverride{DebugData::create()},
m_useSourceLocationFrom{UseSourceLocationFrom::Comments} m_useSourceLocationFrom{UseSourceLocationFrom::Comments}
{} {}
@ -144,7 +144,7 @@ protected:
private: private:
Dialect const& m_dialect; Dialect const& m_dialect;
std::optional<std::map<unsigned, std::shared_ptr<langutil::CharStream>>> m_charStreamMap; std::optional<std::map<unsigned, std::shared_ptr<std::string const>>> m_sourceNames;
langutil::SourceLocation m_locationOverride; langutil::SourceLocation m_locationOverride;
std::shared_ptr<DebugData const> m_debugDataOverride; std::shared_ptr<DebugData const> m_debugDataOverride;
UseSourceLocationFrom m_useSourceLocationFrom = UseSourceLocationFrom::Scanner; UseSourceLocationFrom m_useSourceLocationFrom = UseSourceLocationFrom::Scanner;

View File

@ -88,18 +88,20 @@ evmasm::Assembly::OptimiserSettings translateOptimiserSettings(
} }
Scanner const& AssemblyStack::scanner() const CharStream const& AssemblyStack::charStream(string const& _sourceName) const
{ {
yulAssert(m_scanner, ""); yulAssert(m_charStream, "");
return *m_scanner; yulAssert(m_charStream->name() == _sourceName, "");
return *m_charStream;
} }
bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source) bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source)
{ {
m_errors.clear(); m_errors.clear();
m_analysisSuccessful = false; m_analysisSuccessful = false;
m_scanner = make_shared<Scanner>(CharStream(_source, _sourceName)); m_charStream = make_unique<CharStream>(_source, _sourceName);
m_parserResult = ObjectParser(m_errorReporter, languageToDialect(m_language, m_evmVersion)).parse(m_scanner, false); shared_ptr<Scanner> scanner = make_shared<Scanner>(*m_charStream);
m_parserResult = ObjectParser(m_errorReporter, languageToDialect(m_language, m_evmVersion)).parse(scanner, false);
if (!m_errorReporter.errors().empty()) if (!m_errorReporter.errors().empty())
return false; return false;
yulAssert(m_parserResult, ""); yulAssert(m_parserResult, "");
@ -132,7 +134,8 @@ void AssemblyStack::translate(AssemblyStack::Language _targetLanguage)
); );
*m_parserResult = EVMToEwasmTranslator( *m_parserResult = EVMToEwasmTranslator(
languageToDialect(m_language, m_evmVersion) languageToDialect(m_language, m_evmVersion),
*this
).run(*parserResult()); ).run(*parserResult());
m_language = _targetLanguage; m_language = _targetLanguage;
@ -241,6 +244,7 @@ AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
{ {
auto [creationAssembly, deployedAssembly] = assembleEVMWithDeployed(_deployName); auto [creationAssembly, deployedAssembly] = assembleEVMWithDeployed(_deployName);
yulAssert(creationAssembly, ""); yulAssert(creationAssembly, "");
yulAssert(m_charStream, "");
MachineAssemblyObject creationObject; MachineAssemblyObject creationObject;
creationObject.bytecode = make_shared<evmasm::LinkerObject>(creationAssembly->assemble()); creationObject.bytecode = make_shared<evmasm::LinkerObject>(creationAssembly->assemble());
@ -249,7 +253,7 @@ AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
creationObject.sourceMappings = make_unique<string>( creationObject.sourceMappings = make_unique<string>(
evmasm::AssemblyItem::computeSourceMapping( evmasm::AssemblyItem::computeSourceMapping(
creationAssembly->items(), creationAssembly->items(),
{{scanner().charStream() ? scanner().charStream()->name() : "", 0}} {{m_charStream->name(), 0}}
) )
); );
@ -261,7 +265,7 @@ AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
deployedObject.sourceMappings = make_unique<string>( deployedObject.sourceMappings = make_unique<string>(
evmasm::AssemblyItem::computeSourceMapping( evmasm::AssemblyItem::computeSourceMapping(
deployedAssembly->items(), deployedAssembly->items(),
{{scanner().charStream() ? scanner().charStream()->name() : "", 0}} {{m_charStream->name(), 0}}
) )
); );
} }

View File

@ -24,6 +24,7 @@
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
#include <liblangutil/CharStreamProvider.h>
#include <libyul/Object.h> #include <libyul/Object.h>
#include <libyul/ObjectParser.h> #include <libyul/ObjectParser.h>
@ -61,7 +62,7 @@ struct MachineAssemblyObject
* Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and * Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and
* Ewasm as output. * Ewasm as output.
*/ */
class AssemblyStack class AssemblyStack: public langutil::CharStreamProvider
{ {
public: public:
enum class Language { Yul, Assembly, StrictAssembly, Ewasm }; enum class Language { Yul, Assembly, StrictAssembly, Ewasm };
@ -77,8 +78,8 @@ public:
m_errorReporter(m_errors) m_errorReporter(m_errors)
{} {}
/// @returns the scanner used during parsing /// @returns the char stream used during parsing
langutil::Scanner const& scanner() const; langutil::CharStream const& charStream(std::string const& _sourceName) const override;
/// Runs parsing and analysis steps, returns false if input cannot be assembled. /// Runs parsing and analysis steps, returns false if input cannot be assembled.
/// Multiple calls overwrite the previous state. /// Multiple calls overwrite the previous state.
@ -132,7 +133,7 @@ private:
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
solidity::frontend::OptimiserSettings m_optimiserSettings; solidity::frontend::OptimiserSettings m_optimiserSettings;
std::shared_ptr<langutil::Scanner> m_scanner; std::unique_ptr<langutil::CharStream> m_charStream;
bool m_analysisSuccessful = false; bool m_analysisSuccessful = false;
std::shared_ptr<yul::Object> m_parserResult; std::shared_ptr<yul::Object> m_parserResult;

View File

@ -41,6 +41,7 @@
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
#include <liblangutil/Scanner.h> #include <liblangutil/Scanner.h>
#include <liblangutil/SourceReferenceFormatter.h> #include <liblangutil/SourceReferenceFormatter.h>
#include <liblangutil/CharStreamProvider.h>
#include <libsolidity/interface/OptimiserSettings.h> #include <libsolidity/interface/OptimiserSettings.h>
@ -106,7 +107,7 @@ Object EVMToEwasmTranslator::run(Object const& _object)
message += ret.toString(&WasmDialect::instance()); message += ret.toString(&WasmDialect::instance());
message += "----------------------------------\n"; message += "----------------------------------\n";
for (auto const& err: errors) for (auto const& err: errors)
message += langutil::SourceReferenceFormatter::formatErrorInformation(*err); message += langutil::SourceReferenceFormatter::formatErrorInformation(*err, m_charStreamProvider);
yulAssert(false, message); yulAssert(false, message);
} }
@ -140,7 +141,10 @@ void EVMToEwasmTranslator::parsePolyfill()
{ {
string message; string message;
for (auto const& err: errors) for (auto const& err: errors)
message += langutil::SourceReferenceFormatter::formatErrorInformation(*err); message += langutil::SourceReferenceFormatter::formatErrorInformation(
*err,
SingletonCharStreamProvider(*scanner->charStream())
);
yulAssert(false, message); yulAssert(false, message);
} }

View File

@ -25,6 +25,10 @@
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/Dialect.h> #include <libyul/Dialect.h>
namespace solidity::langutil
{
class CharStreamProvider;
}
namespace solidity::yul namespace solidity::yul
{ {
struct Object; struct Object;
@ -32,13 +36,17 @@ struct Object;
class EVMToEwasmTranslator: public ASTModifier class EVMToEwasmTranslator: public ASTModifier
{ {
public: public:
EVMToEwasmTranslator(Dialect const& _evmDialect): m_dialect(_evmDialect) {} EVMToEwasmTranslator(Dialect const& _evmDialect, langutil::CharStreamProvider const& _charStreamProvider):
m_dialect(_evmDialect),
m_charStreamProvider(_charStreamProvider)
{}
Object run(Object const& _object); Object run(Object const& _object);
private: private:
void parsePolyfill(); void parsePolyfill();
Dialect const& m_dialect; Dialect const& m_dialect;
langutil::CharStreamProvider const& m_charStreamProvider;
std::shared_ptr<Block> m_polyfill; std::shared_ptr<Block> m_polyfill;
std::set<YulString> m_polyfillFunctions; std::set<YulString> m_polyfillFunctions;

View File

@ -561,7 +561,7 @@ bool CommandLineInterface::compile()
m_compiler = make_unique<CompilerStack>(m_fileReader.reader()); m_compiler = make_unique<CompilerStack>(m_fileReader.reader());
SourceReferenceFormatter formatter(serr(false), coloredOutput(m_options), m_options.formatting.withErrorIds); SourceReferenceFormatter formatter(serr(false), *m_compiler, coloredOutput(m_options), m_options.formatting.withErrorIds);
try try
{ {
@ -598,8 +598,7 @@ bool CommandLineInterface::compile()
if (!m_compiler->analyze()) if (!m_compiler->analyze())
{ {
for (auto const& error: m_compiler->errors()) formatter.printErrorInformation(m_compiler->errors());
formatter.printErrorInformation(*error);
astAssert(false, "Analysis of the AST failed"); astAssert(false, "Analysis of the AST failed");
} }
} }
@ -974,7 +973,7 @@ 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), coloredOutput(m_options), m_options.formatting.withErrorIds); SourceReferenceFormatter formatter(serr(false), stack, coloredOutput(m_options), m_options.formatting.withErrorIds);
for (auto const& error: stack.errors()) for (auto const& error: stack.errors())
{ {

View File

@ -57,11 +57,11 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
{ "sub.asm", 1 } { "sub.asm", 1 }
}; };
Assembly _assembly; Assembly _assembly;
auto root_asm = make_shared<CharStream>("lorem ipsum", "root.asm"); auto root_asm = make_shared<string>("root.asm");
_assembly.setSourceLocation({1, 3, root_asm}); _assembly.setSourceLocation({1, 3, root_asm});
Assembly _subAsm; Assembly _subAsm;
auto sub_asm = make_shared<CharStream>("lorem ipsum", "sub.asm"); auto sub_asm = make_shared<string>("sub.asm");
_subAsm.setSourceLocation({6, 8, sub_asm}); _subAsm.setSourceLocation({6, 8, sub_asm});
// PushImmutable // PushImmutable
_subAsm.appendImmutable("someImmutable"); _subAsm.appendImmutable("someImmutable");
@ -172,11 +172,11 @@ BOOST_AUTO_TEST_CASE(immutable)
{ "sub.asm", 1 } { "sub.asm", 1 }
}; };
Assembly _assembly; Assembly _assembly;
auto root_asm = make_shared<CharStream>("lorem ipsum", "root.asm"); auto root_asm = make_shared<string>("root.asm");
_assembly.setSourceLocation({1, 3, root_asm}); _assembly.setSourceLocation({1, 3, root_asm});
Assembly _subAsm; Assembly _subAsm;
auto sub_asm = make_shared<CharStream>("lorem ipsum", "sub.asm"); auto sub_asm = make_shared<string>("sub.asm");
_subAsm.setSourceLocation({6, 8, sub_asm}); _subAsm.setSourceLocation({6, 8, sub_asm});
_subAsm.appendImmutable("someImmutable"); _subAsm.appendImmutable("someImmutable");
_subAsm.appendImmutable("someOtherImmutable"); _subAsm.appendImmutable("someOtherImmutable");

View File

@ -34,9 +34,9 @@ BOOST_AUTO_TEST_SUITE(SourceLocationTest)
BOOST_AUTO_TEST_CASE(test_fail) BOOST_AUTO_TEST_CASE(test_fail)
{ {
auto const source = std::make_shared<CharStream>("lorem ipsum", "source"); auto const source = std::make_shared<std::string>("source");
auto const sourceA = std::make_shared<CharStream>("lorem ipsum", "sourceA"); auto const sourceA = std::make_shared<std::string>("sourceA");
auto const sourceB = std::make_shared<CharStream>("lorem ipsum", "sourceB"); auto const sourceB = std::make_shared<std::string>("sourceB");
BOOST_CHECK(SourceLocation{} == SourceLocation{}); BOOST_CHECK(SourceLocation{} == SourceLocation{});
BOOST_CHECK((SourceLocation{0, 3, sourceA} != SourceLocation{0, 3, sourceB})); BOOST_CHECK((SourceLocation{0, 3, sourceA} != SourceLocation{0, 3, sourceB}));

View File

@ -141,9 +141,8 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi
if (!c.compile(CompilerStack::State::Parsed)) if (!c.compile(CompilerStack::State::Parsed))
{ {
SourceReferenceFormatter formatter(_stream, _formatted, false); SourceReferenceFormatter formatter(_stream, c, _formatted, false);
for (auto const& error: c.errors()) formatter.printErrorInformation(c.errors());
formatter.printErrorInformation(*error);
return TestResult::FatalError; return TestResult::FatalError;
} }
@ -167,9 +166,8 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi
if (m_expectation.empty()) if (m_expectation.empty())
return resultsMatch ? TestResult::Success : TestResult::Failure; return resultsMatch ? TestResult::Success : TestResult::Failure;
SourceReferenceFormatter formatter(_stream, _formatted, false); SourceReferenceFormatter{_stream, c, _formatted, false}
for (auto const& error: c.errors()) .printErrorInformation(c.errors());
formatter.printErrorInformation(*error);
return TestResult::FatalError; return TestResult::FatalError;
} }

View File

@ -151,7 +151,7 @@ string AnalysisFramework::formatErrors() const
string AnalysisFramework::formatError(Error const& _error) const string AnalysisFramework::formatError(Error const& _error) const
{ {
return SourceReferenceFormatter::formatErrorInformation(_error); return SourceReferenceFormatter::formatErrorInformation(_error, *m_compiler);
} }
ContractDefinition const* AnalysisFramework::retrieveContractByName(SourceUnit const& _source, string const& _name) ContractDefinition const* AnalysisFramework::retrieveContractByName(SourceUnit const& _source, string const& _name)

View File

@ -109,7 +109,7 @@ void printAssemblyLocations(AssemblyItems const& _items)
", " << ", " <<
_loc.end << _loc.end <<
", make_shared<string>(\"" << ", make_shared<string>(\"" <<
_loc.source->name() << *_loc.sourceName <<
"\")}) +" << endl; "\")}) +" << endl;
}; };
@ -157,15 +157,16 @@ BOOST_AUTO_TEST_SUITE(Assembly)
BOOST_AUTO_TEST_CASE(location_test) BOOST_AUTO_TEST_CASE(location_test)
{ {
auto sourceCode = make_shared<CharStream>(R"( string sourceCode = R"(
pragma abicoder v1; pragma abicoder v1;
contract test { contract test {
function f() public returns (uint256 a) { function f() public returns (uint256 a) {
return 16; return 16;
} }
} }
)", ""); )";
AssemblyItems items = compileContract(sourceCode); AssemblyItems items = compileContract(make_shared<CharStream>(sourceCode, ""));
shared_ptr<string> sourceName = make_shared<string>();
bool hasShifts = solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting(); bool hasShifts = solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting();
auto codegenCharStream = make_shared<CharStream>("", "--CODEGEN--"); auto codegenCharStream = make_shared<CharStream>("", "--CODEGEN--");
@ -173,18 +174,18 @@ BOOST_AUTO_TEST_CASE(location_test)
vector<SourceLocation> locations; vector<SourceLocation> locations;
if (solidity::test::CommonOptions::get().optimize) if (solidity::test::CommonOptions::get().optimize)
locations = locations =
vector<SourceLocation>(31, SourceLocation{23, 103, sourceCode}) + vector<SourceLocation>(31, SourceLocation{23, 103, sourceName}) +
vector<SourceLocation>(1, SourceLocation{41, 100, sourceCode}) + vector<SourceLocation>(1, SourceLocation{41, 100, sourceName}) +
vector<SourceLocation>(1, SourceLocation{93, 95, sourceCode}) + vector<SourceLocation>(1, SourceLocation{93, 95, sourceName}) +
vector<SourceLocation>(15, SourceLocation{41, 100, sourceCode}); vector<SourceLocation>(15, SourceLocation{41, 100, sourceName});
else else
locations = locations =
vector<SourceLocation>(hasShifts ? 31 : 32, SourceLocation{23, 103, sourceCode}) + vector<SourceLocation>(hasShifts ? 31 : 32, SourceLocation{23, 103, sourceName}) +
vector<SourceLocation>(24, SourceLocation{41, 100, sourceCode}) + vector<SourceLocation>(24, SourceLocation{41, 100, sourceName}) +
vector<SourceLocation>(1, SourceLocation{70, 79, sourceCode}) + vector<SourceLocation>(1, SourceLocation{70, 79, sourceName}) +
vector<SourceLocation>(1, SourceLocation{93, 95, sourceCode}) + vector<SourceLocation>(1, SourceLocation{93, 95, sourceName}) +
vector<SourceLocation>(2, SourceLocation{86, 95, sourceCode}) + vector<SourceLocation>(2, SourceLocation{86, 95, sourceName}) +
vector<SourceLocation>(2, SourceLocation{41, 100, sourceCode}); vector<SourceLocation>(2, SourceLocation{41, 100, sourceName});
checkAssemblyLocations(items, locations); checkAssemblyLocations(items, locations);
} }

View File

@ -118,9 +118,8 @@ TestCase::TestResult GasTest::run(ostream& _stream, string const& _linePrefix, b
if (!compiler().parseAndAnalyze() || !compiler().compile()) if (!compiler().parseAndAnalyze() || !compiler().compile())
{ {
SourceReferenceFormatter formatter(_stream, _formatted, false); SourceReferenceFormatter{_stream, compiler(), _formatted, false}
for (auto const& error: compiler().errors()) .printErrorInformation(compiler().errors());
formatter.printErrorInformation(*error);
return TestResult::FatalError; return TestResult::FatalError;
} }

View File

@ -81,7 +81,7 @@ std::optional<Error> parseAndReturnFirstError(
{ {
string errors; string errors;
for (auto const& err: stack.errors()) for (auto const& err: stack.errors())
errors += SourceReferenceFormatter::formatErrorInformation(*err); errors += SourceReferenceFormatter::formatErrorInformation(*err, stack);
BOOST_FAIL("Found more than one error:\n" + errors); BOOST_FAIL("Found more than one error:\n" + errors);
} }
error = e; error = e;

View File

@ -65,10 +65,8 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
for (auto const& error: m_compiler.errors()) for (auto const& error: m_compiler.errors())
if (error->type() == langutil::Error::Type::CodeGenerationError) if (error->type() == langutil::Error::Type::CodeGenerationError)
BOOST_THROW_EXCEPTION(*error); BOOST_THROW_EXCEPTION(*error);
langutil::SourceReferenceFormatter formatter(std::cerr, true, false); langutil::SourceReferenceFormatter{std::cerr, m_compiler, true, false}
.printErrorInformation(m_compiler.errors());
for (auto const& error: m_compiler.errors())
formatter.printErrorInformation(*error);
BOOST_ERROR("Compiling contract failed"); BOOST_ERROR("Compiling contract failed");
} }
string contractName(_contractName.empty() ? m_compiler.lastContractName(_mainSourceName) : _contractName); string contractName(_contractName.empty() ? m_compiler.lastContractName(_mainSourceName) : _contractName);

View File

@ -591,19 +591,21 @@ BOOST_AUTO_TEST_CASE(inline_asm_end_location)
class CheckInlineAsmLocation: public ASTConstVisitor class CheckInlineAsmLocation: public ASTConstVisitor
{ {
public: public:
explicit CheckInlineAsmLocation(string _sourceCode): m_sourceCode(_sourceCode) {}
bool visited = false; bool visited = false;
bool visit(InlineAssembly const& _inlineAsm) override bool visit(InlineAssembly const& _inlineAsm) override
{ {
auto loc = _inlineAsm.location(); auto loc = _inlineAsm.location();
auto asmStr = loc.source->source().substr(static_cast<size_t>(loc.start), static_cast<size_t>(loc.end - loc.start)); auto asmStr = m_sourceCode.substr(static_cast<size_t>(loc.start), static_cast<size_t>(loc.end - loc.start));
BOOST_CHECK_EQUAL(asmStr, "assembly { a := 0x12345678 }"); BOOST_CHECK_EQUAL(asmStr, "assembly { a := 0x12345678 }");
visited = true; visited = true;
return false; return false;
} }
string m_sourceCode;
}; };
CheckInlineAsmLocation visitor; CheckInlineAsmLocation visitor{sourceCode};
contract->accept(visitor); contract->accept(visitor);
BOOST_CHECK_MESSAGE(visitor.visited, "No inline asm block found?!"); BOOST_CHECK_MESSAGE(visitor.visited, "No inline asm block found?!");

View File

@ -17,6 +17,7 @@
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
#include <test/libsolidity/SyntaxTest.h> #include <test/libsolidity/SyntaxTest.h>
#include <test/Common.h> #include <test/Common.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
@ -119,11 +120,13 @@ void SyntaxTest::filterObtainedErrors()
string sourceName; string sourceName;
if (auto location = boost::get_error_info<errinfo_sourceLocation>(*currentError)) if (auto location = boost::get_error_info<errinfo_sourceLocation>(*currentError))
{ {
solAssert(location->source, ""); solAssert(location->sourceName, "");
sourceName = location->source->name(); sourceName = *location->sourceName;
solAssert(m_sources.sources.count(sourceName) == 1, ""); solAssert(m_sources.sources.count(sourceName) == 1, "");
int preambleSize = static_cast<int>(location->source->source().size()) - static_cast<int>(m_sources.sources[sourceName].size());
int preambleSize =
static_cast<int>(compiler().charStream(sourceName).size()) -
static_cast<int>(m_sources.sources[sourceName].size());
solAssert(preambleSize >= 0, ""); solAssert(preambleSize >= 0, "");
// ignore the version & license pragma inserted by the testing tool when calculating locations. // ignore the version & license pragma inserted by the testing tool when calculating locations.

View File

@ -53,15 +53,6 @@ Dialect const& defaultDialect(bool _yul)
} }
} }
void yul::test::printErrors(ErrorList const& _errors)
{
SourceReferenceFormatter formatter(cout, true, false);
for (auto const& error: _errors)
formatter.printErrorInformation(*error);
}
pair<shared_ptr<Block>, shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(string const& _source, bool _yul) pair<shared_ptr<Block>, shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(string const& _source, bool _yul)
{ {
AssemblyStack stack( AssemblyStack stack(

View File

@ -44,8 +44,6 @@ struct Dialect;
namespace solidity::yul::test namespace solidity::yul::test
{ {
void printErrors(langutil::ErrorList const& _errors);
std::pair<std::shared_ptr<Block>, std::shared_ptr<AsmAnalysisInfo>> std::pair<std::shared_ptr<Block>, std::shared_ptr<AsmAnalysisInfo>>
parse(std::string const& _source, bool _yul = true); parse(std::string const& _source, bool _yul = true);

View File

@ -51,9 +51,8 @@ TestCase::TestResult EVMCodeTransformTest::run(ostream& _stream, string const& _
if (!stack.parseAndAnalyze("", m_source)) if (!stack.parseAndAnalyze("", m_source))
{ {
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl;
SourceReferenceFormatter formatter(_stream, true, false); SourceReferenceFormatter{_stream, stack, true, false}
for (auto const& error: stack.errors()) .printErrorInformation(stack.errors());
formatter.printErrorInformation(*error);
return TestResult::FatalError; return TestResult::FatalError;
} }

View File

@ -63,7 +63,8 @@ TestCase::TestResult EwasmTranslationTest::run(ostream& _stream, string const& _
return TestResult::FatalError; return TestResult::FatalError;
*m_object = EVMToEwasmTranslator( *m_object = EVMToEwasmTranslator(
EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()) EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()),
m_stack
).run(*m_object); ).run(*m_object);
// Add call to "main()". // Add call to "main()".
@ -78,20 +79,21 @@ TestCase::TestResult EwasmTranslationTest::run(ostream& _stream, string const& _
bool EwasmTranslationTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) bool EwasmTranslationTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted)
{ {
AssemblyStack stack( m_stack = AssemblyStack(
solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().evmVersion(),
AssemblyStack::Language::StrictAssembly, AssemblyStack::Language::StrictAssembly,
solidity::frontend::OptimiserSettings::none() solidity::frontend::OptimiserSettings::none()
); );
if (stack.parseAndAnalyze("", m_source)) if (m_stack.parseAndAnalyze("", m_source))
{ {
m_object = stack.parserResult(); m_object = m_stack.parserResult();
return true; return true;
} }
else else
{ {
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl;
printErrors(_stream, stack.errors()); SourceReferenceFormatter{_stream, m_stack, true, false}
.printErrorInformation(m_stack.errors());
return false; return false;
} }
} }
@ -114,11 +116,3 @@ string EwasmTranslationTest::interpret()
state.dumpTraceAndState(result); state.dumpTraceAndState(result);
return result.str(); return result.str();
} }
void EwasmTranslationTest::printErrors(ostream& _stream, ErrorList const& _errors)
{
SourceReferenceFormatter formatter(_stream, true, false);
for (auto const& error: _errors)
formatter.printErrorInformation(*error);
}

View File

@ -20,16 +20,12 @@
#include <test/TestCase.h> #include <test/TestCase.h>
namespace solidity::langutil #include <libyul/AssemblyStack.h>
{
class Scanner;
class Error;
using ErrorList = std::vector<std::shared_ptr<Error const>>;
}
namespace solidity::yul namespace solidity::yul
{ {
struct Object; struct Object;
class AssemblyStack;
} }
namespace solidity::yul::test namespace solidity::yul::test
@ -51,9 +47,8 @@ private:
bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted);
std::string interpret(); std::string interpret();
static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors);
std::shared_ptr<Object> m_object; std::shared_ptr<Object> m_object;
AssemblyStack m_stack;
}; };
} }

View File

@ -69,7 +69,8 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li
if (!stack.parseAndAnalyze("source", m_source)) if (!stack.parseAndAnalyze("source", m_source))
{ {
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl;
printErrors(_stream, stack.errors()); SourceReferenceFormatter{_stream, stack, true, false}
.printErrorInformation(stack.errors());
return TestResult::FatalError; return TestResult::FatalError;
} }
stack.optimize(); stack.optimize();
@ -104,11 +105,3 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li
return checkResult(_stream, _linePrefix, _formatted); return checkResult(_stream, _linePrefix, _formatted);
} }
void ObjectCompilerTest::printErrors(ostream& _stream, ErrorList const& _errors)
{
SourceReferenceFormatter formatter(_stream, true, false);
for (auto const& error: _errors)
formatter.printErrorInformation(*error);
}

View File

@ -54,8 +54,6 @@ private:
bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted);
void disambiguate(); void disambiguate();
static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors);
frontend::OptimisationPreset m_optimisationPreset; frontend::OptimisationPreset m_optimisationPreset;
bool m_wasm = false; bool m_wasm = false;
}; };

View File

@ -51,24 +51,19 @@ namespace solidity::yul::test
namespace namespace
{ {
string_view constexpr g_strAlternateSourceText = "{}";
shared_ptr<Block> parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorReporter) shared_ptr<Block> parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorReporter)
{ {
try try
{ {
auto scanner = make_shared<Scanner>(CharStream(_source, "")); auto scanner = make_shared<Scanner>(CharStream(_source, ""));
map<unsigned, shared_ptr<CharStream>> indicesToCharStreams; map<unsigned, shared_ptr<string const>> indicesToSourceNames;
indicesToCharStreams[0] = scanner->charStream(); indicesToSourceNames[0] = make_shared<string const>("source0");
indicesToCharStreams[1] = make_shared<CharStream>( indicesToSourceNames[1] = make_shared<string const>("source1");
string(g_strAlternateSourceText.data(), g_strAlternateSourceText.size()),
"alternate.sol"
);
auto parserResult = yul::Parser( auto parserResult = yul::Parser(
errorReporter, errorReporter,
_dialect, _dialect,
move(indicesToCharStreams) move(indicesToSourceNames)
).parse(scanner, false); ).parse(scanner, false);
if (parserResult) if (parserResult)
{ {
@ -200,9 +195,9 @@ BOOST_AUTO_TEST_CASE(default_types_set)
); );
} }
#define CHECK_LOCATION(_actual, _sourceText, _start, _end) \ #define CHECK_LOCATION(_actual, _sourceName, _start, _end) \
do { \ do { \
BOOST_CHECK_EQUAL((_sourceText), ((_actual).source ? (_actual).source->source() : "")); \ BOOST_CHECK_EQUAL((_sourceName), ((_actual).sourceName ? *(_actual).sourceName : "")); \
BOOST_CHECK_EQUAL((_start), (_actual).start); \ BOOST_CHECK_EQUAL((_start), (_actual).start); \
BOOST_CHECK_EQUAL((_end), (_actual).end); \ BOOST_CHECK_EQUAL((_end), (_actual).end); \
} while (0) } while (0)
@ -217,7 +212,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_empty_block)
EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
shared_ptr<Block> result = parse(sourceText, dialect, reporter); shared_ptr<Block> result = parse(sourceText, dialect, reporter);
BOOST_REQUIRE(!!result); BOOST_REQUIRE(!!result);
CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); CHECK_LOCATION(result->debugData->location, "source0", 234, 543);
} }
BOOST_AUTO_TEST_CASE(customSourceLocations_block_with_children) BOOST_AUTO_TEST_CASE(customSourceLocations_block_with_children)
@ -235,12 +230,12 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_with_children)
EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
shared_ptr<Block> result = parse(sourceText, dialect, reporter); shared_ptr<Block> result = parse(sourceText, dialect, reporter);
BOOST_REQUIRE(!!result); BOOST_REQUIRE(!!result);
CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); CHECK_LOCATION(result->debugData->location, "source0", 234, 543);
BOOST_REQUIRE_EQUAL(3, result->statements.size()); BOOST_REQUIRE_EQUAL(3, result->statements.size());
CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 234, 543); CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 234, 543);
CHECK_LOCATION(locationOf(result->statements.at(1)), sourceText, 123, 432); CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 123, 432);
// [2] is inherited source location // [2] is inherited source location
CHECK_LOCATION(locationOf(result->statements.at(2)), sourceText, 123, 432); CHECK_LOCATION(locationOf(result->statements.at(2)), "source0", 123, 432);
} }
BOOST_AUTO_TEST_CASE(customSourceLocations_block_different_sources) BOOST_AUTO_TEST_CASE(customSourceLocations_block_different_sources)
@ -258,12 +253,12 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_different_sources)
EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
shared_ptr<Block> result = parse(sourceText, dialect, reporter); shared_ptr<Block> result = parse(sourceText, dialect, reporter);
BOOST_REQUIRE(!!result); BOOST_REQUIRE(!!result);
CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); CHECK_LOCATION(result->debugData->location, "source0", 234, 543);
BOOST_REQUIRE_EQUAL(3, result->statements.size()); BOOST_REQUIRE_EQUAL(3, result->statements.size());
CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 234, 543); CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 234, 543);
CHECK_LOCATION(locationOf(result->statements.at(1)), g_strAlternateSourceText, 123, 432); CHECK_LOCATION(locationOf(result->statements.at(1)), "source1", 123, 432);
// [2] is inherited source location // [2] is inherited source location
CHECK_LOCATION(locationOf(result->statements.at(2)), g_strAlternateSourceText, 123, 432); CHECK_LOCATION(locationOf(result->statements.at(2)), "source1", 123, 432);
} }
BOOST_AUTO_TEST_CASE(customSourceLocations_block_nested) BOOST_AUTO_TEST_CASE(customSourceLocations_block_nested)
@ -280,9 +275,9 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_nested)
EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
shared_ptr<Block> result = parse(sourceText, dialect, reporter); shared_ptr<Block> result = parse(sourceText, dialect, reporter);
BOOST_REQUIRE(!!result); BOOST_REQUIRE(!!result);
CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); CHECK_LOCATION(result->debugData->location, "source0", 234, 543);
BOOST_REQUIRE_EQUAL(2, result->statements.size()); BOOST_REQUIRE_EQUAL(2, result->statements.size());
CHECK_LOCATION(locationOf(result->statements.at(1)), sourceText, 343, 434); CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 343, 434);
} }
BOOST_AUTO_TEST_CASE(customSourceLocations_block_switch_case) BOOST_AUTO_TEST_CASE(customSourceLocations_block_switch_case)
@ -304,19 +299,19 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_switch_case)
EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
shared_ptr<Block> result = parse(sourceText, dialect, reporter); shared_ptr<Block> result = parse(sourceText, dialect, reporter);
BOOST_REQUIRE(!!result); BOOST_REQUIRE(!!result);
CHECK_LOCATION(result->debugData->location, sourceText, 234, 543); CHECK_LOCATION(result->debugData->location, "source0", 234, 543);
BOOST_REQUIRE_EQUAL(2, result->statements.size()); BOOST_REQUIRE_EQUAL(2, result->statements.size());
BOOST_REQUIRE(holds_alternative<Switch>(result->statements.at(1))); BOOST_REQUIRE(holds_alternative<Switch>(result->statements.at(1)));
auto const& switchStmt = get<Switch>(result->statements.at(1)); auto const& switchStmt = get<Switch>(result->statements.at(1));
CHECK_LOCATION(switchStmt.debugData->location, sourceText, 343, 434); CHECK_LOCATION(switchStmt.debugData->location, "source0", 343, 434);
BOOST_REQUIRE_EQUAL(1, switchStmt.cases.size()); BOOST_REQUIRE_EQUAL(1, switchStmt.cases.size());
CHECK_LOCATION(switchStmt.cases.at(0).debugData->location, sourceText, 3141, 59265); CHECK_LOCATION(switchStmt.cases.at(0).debugData->location, "source0", 3141, 59265);
auto const& caseBody = switchStmt.cases.at(0).body; auto const& caseBody = switchStmt.cases.at(0).body;
BOOST_REQUIRE_EQUAL(1, caseBody.statements.size()); BOOST_REQUIRE_EQUAL(1, caseBody.statements.size());
CHECK_LOCATION(locationOf(caseBody.statements.at(0)), sourceText, 271, 828); CHECK_LOCATION(locationOf(caseBody.statements.at(0)), "source0", 271, 828);
} }
BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope) BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope)
@ -337,19 +332,19 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope)
shared_ptr<Block> result = parse(sourceText, dialect, reporter); shared_ptr<Block> result = parse(sourceText, dialect, reporter);
BOOST_REQUIRE(!!result); BOOST_REQUIRE(!!result);
CHECK_LOCATION(result->debugData->location, sourceText, 1, 100); CHECK_LOCATION(result->debugData->location, "source0", 1, 100);
BOOST_REQUIRE_EQUAL(3, result->statements.size()); BOOST_REQUIRE_EQUAL(3, result->statements.size());
CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 1, 100); CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 1, 100);
// First child element must be a block itself with one statement. // First child element must be a block itself with one statement.
BOOST_REQUIRE(holds_alternative<Block>(result->statements.at(0))); BOOST_REQUIRE(holds_alternative<Block>(result->statements.at(0)));
BOOST_REQUIRE_EQUAL(get<Block>(result->statements.at(0)).statements.size(), 1); BOOST_REQUIRE_EQUAL(get<Block>(result->statements.at(0)).statements.size(), 1);
CHECK_LOCATION(locationOf(get<Block>(result->statements.at(0)).statements.at(0)), sourceText, 123, 432); CHECK_LOCATION(locationOf(get<Block>(result->statements.at(0)).statements.at(0)), "source0", 123, 432);
// The next two elements have an inherited source location from the prior inner scope. // The next two elements have an inherited source location from the prior inner scope.
CHECK_LOCATION(locationOf(result->statements.at(1)), sourceText, 123, 432); CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 123, 432);
CHECK_LOCATION(locationOf(result->statements.at(2)), sourceText, 123, 432); CHECK_LOCATION(locationOf(result->statements.at(2)), "source0", 123, 432);
} }
BOOST_AUTO_TEST_CASE(customSourceLocations_assign_empty) BOOST_AUTO_TEST_CASE(customSourceLocations_assign_empty)
@ -368,8 +363,8 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_assign_empty)
shared_ptr<Block> result = parse(sourceText, dialect, reporter); shared_ptr<Block> result = parse(sourceText, dialect, reporter);
BOOST_REQUIRE(!!result); // should still parse BOOST_REQUIRE(!!result); // should still parse
BOOST_REQUIRE_EQUAL(2, result->statements.size()); BOOST_REQUIRE_EQUAL(2, result->statements.size());
CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 123, 432); CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 123, 432);
CHECK_LOCATION(locationOf(result->statements.at(1)), g_strAlternateSourceText, 1, 10); CHECK_LOCATION(locationOf(result->statements.at(1)), "source1", 1, 10);
} }
BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_source_index) BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_source_index)
@ -407,10 +402,10 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_1)
BOOST_REQUIRE(!!result); BOOST_REQUIRE(!!result);
BOOST_REQUIRE_EQUAL(1, result->statements.size()); BOOST_REQUIRE_EQUAL(1, result->statements.size());
CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 123, 432); CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 123, 432);
BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0))); BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0)));
VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0)); VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0));
CHECK_LOCATION(locationOf(*varDecl.value), sourceText, 234, 2026); CHECK_LOCATION(locationOf(*varDecl.value), "source0", 234, 2026);
} }
BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_2) BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_2)
@ -429,20 +424,20 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_2)
shared_ptr<Block> result = parse(sourceText, dialect, reporter); shared_ptr<Block> result = parse(sourceText, dialect, reporter);
BOOST_REQUIRE(!!result); BOOST_REQUIRE(!!result);
BOOST_REQUIRE_EQUAL(1, result->statements.size()); BOOST_REQUIRE_EQUAL(1, result->statements.size());
CHECK_LOCATION(result->debugData->location, sourceText, 0, 5); CHECK_LOCATION(result->debugData->location, "source0", 0, 5);
// `let x := add(1, ` // `let x := add(1, `
BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0))); BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0)));
VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0)); VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0));
CHECK_LOCATION(varDecl.debugData->location, sourceText, 0, 5); CHECK_LOCATION(varDecl.debugData->location, "source0", 0, 5);
BOOST_REQUIRE(!!varDecl.value); BOOST_REQUIRE(!!varDecl.value);
BOOST_REQUIRE(holds_alternative<FunctionCall>(*varDecl.value)); BOOST_REQUIRE(holds_alternative<FunctionCall>(*varDecl.value));
FunctionCall const& call = get<FunctionCall>(*varDecl.value); FunctionCall const& call = get<FunctionCall>(*varDecl.value);
CHECK_LOCATION(call.debugData->location, g_strAlternateSourceText, 2, 3); CHECK_LOCATION(call.debugData->location, "source1", 2, 3);
// `2` // `2`
BOOST_REQUIRE_EQUAL(2, call.arguments.size()); BOOST_REQUIRE_EQUAL(2, call.arguments.size());
CHECK_LOCATION(locationOf(call.arguments.at(1)), sourceText, 4, 8); CHECK_LOCATION(locationOf(call.arguments.at(1)), "source0", 4, 8);
} }
BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_3) BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_3)
@ -463,24 +458,24 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_3)
shared_ptr<Block> result = parse(sourceText, dialect, reporter); shared_ptr<Block> result = parse(sourceText, dialect, reporter);
BOOST_REQUIRE(!!result); BOOST_REQUIRE(!!result);
BOOST_REQUIRE_EQUAL(2, result->statements.size()); BOOST_REQUIRE_EQUAL(2, result->statements.size());
CHECK_LOCATION(result->debugData->location, g_strAlternateSourceText, 23, 45); CHECK_LOCATION(result->debugData->location, "source1", 23, 45);
BOOST_REQUIRE(holds_alternative<Block>(result->statements.at(0))); BOOST_REQUIRE(holds_alternative<Block>(result->statements.at(0)));
Block const& innerBlock = get<Block>(result->statements.at(0)); Block const& innerBlock = get<Block>(result->statements.at(0));
CHECK_LOCATION(innerBlock.debugData->location, g_strAlternateSourceText, 23, 45); CHECK_LOCATION(innerBlock.debugData->location, "source1", 23, 45);
BOOST_REQUIRE_EQUAL(1, innerBlock.statements.size()); BOOST_REQUIRE_EQUAL(1, innerBlock.statements.size());
BOOST_REQUIRE(holds_alternative<ExpressionStatement>(result->statements.at(1))); BOOST_REQUIRE(holds_alternative<ExpressionStatement>(result->statements.at(1)));
ExpressionStatement const& sstoreStmt = get<ExpressionStatement>(innerBlock.statements.at(0)); ExpressionStatement const& sstoreStmt = get<ExpressionStatement>(innerBlock.statements.at(0));
BOOST_REQUIRE(holds_alternative<FunctionCall>(sstoreStmt.expression)); BOOST_REQUIRE(holds_alternative<FunctionCall>(sstoreStmt.expression));
FunctionCall const& sstoreCall = get<FunctionCall>(sstoreStmt.expression); FunctionCall const& sstoreCall = get<FunctionCall>(sstoreStmt.expression);
CHECK_LOCATION(sstoreCall.debugData->location, g_strAlternateSourceText, 23, 45); CHECK_LOCATION(sstoreCall.debugData->location, "source1", 23, 45);
BOOST_REQUIRE(holds_alternative<ExpressionStatement>(result->statements.at(1))); BOOST_REQUIRE(holds_alternative<ExpressionStatement>(result->statements.at(1)));
ExpressionStatement mstoreStmt = get<ExpressionStatement>(result->statements.at(1)); ExpressionStatement mstoreStmt = get<ExpressionStatement>(result->statements.at(1));
BOOST_REQUIRE(holds_alternative<FunctionCall>(mstoreStmt.expression)); BOOST_REQUIRE(holds_alternative<FunctionCall>(mstoreStmt.expression));
FunctionCall const& mstoreCall = get<FunctionCall>(mstoreStmt.expression); FunctionCall const& mstoreCall = get<FunctionCall>(mstoreStmt.expression);
CHECK_LOCATION(mstoreCall.debugData->location, sourceText, 420, 680); CHECK_LOCATION(mstoreCall.debugData->location, "source0", 420, 680);
} }
BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_comments_after_valid) BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_comments_after_valid)
@ -499,11 +494,11 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_comments_after_valid)
shared_ptr<Block> result = parse(sourceText, dialect, reporter); shared_ptr<Block> result = parse(sourceText, dialect, reporter);
BOOST_REQUIRE(!!result); BOOST_REQUIRE(!!result);
BOOST_REQUIRE_EQUAL(1, result->statements.size()); BOOST_REQUIRE_EQUAL(1, result->statements.size());
CHECK_LOCATION(result->debugData->location, g_strAlternateSourceText, 23, 45); CHECK_LOCATION(result->debugData->location, "source1", 23, 45);
BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0))); BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0)));
VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0)); VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0));
CHECK_LOCATION(varDecl.debugData->location, sourceText, 420, 680); CHECK_LOCATION(varDecl.debugData->location, "source0", 420, 680);
} }
BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_suffix) BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_suffix)
@ -553,7 +548,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_ensure_last_match)
VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0)); VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0));
// Ensure the latest @src per documentation-comment is used (0:30:40). // Ensure the latest @src per documentation-comment is used (0:30:40).
CHECK_LOCATION(varDecl.debugData->location, sourceText, 30, 40); CHECK_LOCATION(varDecl.debugData->location, "source0", 30, 40);
} }
BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc) BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc)
@ -573,6 +568,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc)
BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0))); BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0)));
VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0)); VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0));
// -1 points to original source code, which in this case is `"source0"` (which is also
CHECK_LOCATION(varDecl.debugData->location, "", 10, 20); CHECK_LOCATION(varDecl.debugData->location, "", 10, 20);
} }

View File

@ -78,7 +78,8 @@ bool YulInterpreterTest::parse(ostream& _stream, string const& _linePrefix, bool
else else
{ {
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl;
printErrors(_stream, stack.errors()); SourceReferenceFormatter{_stream, stack, true, false}
.printErrorInformation(stack.errors());
return false; return false;
} }
} }
@ -101,11 +102,3 @@ string YulInterpreterTest::interpret()
state.dumpTraceAndState(result); state.dumpTraceAndState(result);
return result.str(); return result.str();
} }
void YulInterpreterTest::printErrors(ostream& _stream, ErrorList const& _errors)
{
SourceReferenceFormatter formatter(_stream, true, false);
for (auto const& error: _errors)
formatter.printErrorInformation(*error);
}

View File

@ -20,13 +20,6 @@
#include <test/TestCase.h> #include <test/TestCase.h>
namespace solidity::langutil
{
class Scanner;
class Error;
using ErrorList = std::vector<std::shared_ptr<Error const>>;
}
namespace solidity::yul namespace solidity::yul
{ {
struct AsmAnalysisInfo; struct AsmAnalysisInfo;
@ -52,8 +45,6 @@ private:
bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted);
std::string interpret(); std::string interpret();
static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors);
std::shared_ptr<Block> m_ast; std::shared_ptr<Block> m_ast;
std::shared_ptr<AsmAnalysisInfo> m_analysisInfo; std::shared_ptr<AsmAnalysisInfo> m_analysisInfo;
}; };

View File

@ -28,6 +28,7 @@
#include <libyul/AsmPrinter.h> #include <libyul/AsmPrinter.h>
#include <liblangutil/SourceReferenceFormatter.h> #include <liblangutil/SourceReferenceFormatter.h>
#include <liblangutil/Scanner.h>
#include <libsolutil/AnsiColorized.h> #include <libsolutil/AnsiColorized.h>
@ -115,16 +116,10 @@ std::pair<std::shared_ptr<Object>, std::shared_ptr<AsmAnalysisInfo>> YulOptimize
if (!object || !analysisInfo || !Error::containsOnlyWarnings(errors)) if (!object || !analysisInfo || !Error::containsOnlyWarnings(errors))
{ {
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl;
printErrors(_stream, errors); CharStream charStream(_source, "");
SourceReferenceFormatter{_stream, SingletonCharStreamProvider(charStream), true, false}
.printErrorInformation(errors);
return {}; return {};
} }
return {std::move(object), std::move(analysisInfo)}; return {std::move(object), std::move(analysisInfo)};
} }
void YulOptimizerTest::printErrors(ostream& _stream, ErrorList const& _errors)
{
SourceReferenceFormatter formatter(_stream, true, false);
for (auto const& error: _errors)
formatter.printErrorInformation(*error);
}

View File

@ -51,7 +51,6 @@ private:
std::pair<std::shared_ptr<Object>, std::shared_ptr<AsmAnalysisInfo>> parse( std::pair<std::shared_ptr<Object>, std::shared_ptr<AsmAnalysisInfo>> parse(
std::ostream& _stream, std::string const& _linePrefix, bool const _formatted, std::string const& _source std::ostream& _stream, std::string const& _linePrefix, bool const _formatted, std::string const& _source
); );
static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors);
std::string m_optimizerStep; std::string m_optimizerStep;

View File

@ -41,13 +41,11 @@ optional<CompilerOutput> SolidityCompilationFramework::compileContract()
{ {
if (m_compilerInput.debugFailure) if (m_compilerInput.debugFailure)
{ {
SourceReferenceFormatter formatter(cerr, false, false);
cerr << "Compiling contract failed" << endl; cerr << "Compiling contract failed" << endl;
for (auto const& error: m_compiler.errors()) for (auto const& error: m_compiler.errors())
formatter.printExceptionInformation( cerr << SourceReferenceFormatter::formatErrorInformation(
*error, *error,
formatter.formatErrorInformation(*error) m_compiler
); );
} }
return {}; return {};

View File

@ -43,20 +43,6 @@ using namespace solidity::yul;
using namespace solidity::yul::test; using namespace solidity::yul::test;
using namespace solidity::yul::test::yul_fuzzer; using namespace solidity::yul::test::yul_fuzzer;
namespace
{
void printErrors(ostream& _stream, ErrorList const& _errors)
{
SourceReferenceFormatter formatter(_stream, false, false);
for (auto const& error: _errors)
formatter.printExceptionInformation(
*error,
(error->type() == Error::Type::Warning) ? "Warning" : "Error"
);
}
}
DEFINE_PROTO_FUZZER(Program const& _input) DEFINE_PROTO_FUZZER(Program const& _input)
{ {
ProtoConverter converter; ProtoConverter converter;
@ -88,7 +74,13 @@ DEFINE_PROTO_FUZZER(Program const& _input)
!Error::containsOnlyWarnings(stack.errors()) !Error::containsOnlyWarnings(stack.errors())
) )
{ {
printErrors(std::cout, stack.errors()); SourceReferenceFormatter formatter(std::cout, stack, false, false);
for (auto const& error: stack.errors())
formatter.printExceptionInformation(
*error,
(error->type() == Error::Type::Warning) ? "Warning" : "Error"
);
yulAssert(false, "Proto fuzzer generated malformed program"); yulAssert(false, "Proto fuzzer generated malformed program");
} }

View File

@ -44,6 +44,8 @@
#include <libsolutil/JSON.h> #include <libsolutil/JSON.h>
#include <libsolidity/interface/OptimiserSettings.h> #include <libsolidity/interface/OptimiserSettings.h>
#include <liblangutil/CharStreamProvider.h>
#include <liblangutil/Scanner.h>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/join.hpp> #include <boost/algorithm/string/join.hpp>
@ -78,17 +80,19 @@ class YulOpti
public: public:
void printErrors() void printErrors()
{ {
SourceReferenceFormatter formatter(cerr, true, false); SourceReferenceFormatter{
cerr,
for (auto const& error: m_errors) SingletonCharStreamProvider(*m_scanner->charStream()),
formatter.printErrorInformation(*error); true,
false
}.printErrorInformation(m_errors);
} }
bool parse(string const& _input) bool parse(string const& _input)
{ {
ErrorReporter errorReporter(m_errors); ErrorReporter errorReporter(m_errors);
shared_ptr<Scanner> scanner = make_shared<Scanner>(CharStream(_input, "")); m_scanner = make_shared<Scanner>(CharStream(_input, ""));
m_ast = yul::Parser(errorReporter, m_dialect).parse(scanner, false); m_ast = yul::Parser(errorReporter, m_dialect).parse(m_scanner, false);
if (!m_ast || !errorReporter.errors().empty()) if (!m_ast || !errorReporter.errors().empty())
{ {
cerr << "Error parsing source." << endl; cerr << "Error parsing source." << endl;
@ -230,6 +234,7 @@ public:
private: private:
ErrorList m_errors; ErrorList m_errors;
shared_ptr<Scanner> m_scanner;
shared_ptr<yul::Block> m_ast; shared_ptr<yul::Block> m_ast;
Dialect const& m_dialect{EVMDialect::strictAssemblyForEVMObjects(EVMVersion{})}; Dialect const& m_dialect{EVMDialect::strictAssemblyForEVMObjects(EVMVersion{})};
shared_ptr<AsmAnalysisInfo> m_analysisInfo; shared_ptr<AsmAnalysisInfo> m_analysisInfo;

View File

@ -53,12 +53,6 @@ namespace po = boost::program_options;
namespace namespace
{ {
void printErrors(ErrorList const& _errors)
{
for (auto const& error: _errors)
SourceReferenceFormatter(cout, true, false).printErrorInformation(*error);
}
pair<shared_ptr<Block>, shared_ptr<AsmAnalysisInfo>> parse(string const& _source) pair<shared_ptr<Block>, shared_ptr<AsmAnalysisInfo>> parse(string const& _source)
{ {
AssemblyStack stack( AssemblyStack stack(
@ -73,7 +67,7 @@ pair<shared_ptr<Block>, shared_ptr<AsmAnalysisInfo>> parse(string const& _source
} }
else else
{ {
printErrors(stack.errors()); SourceReferenceFormatter(cout, stack, true, false).printErrorInformation(stack.errors());
return {}; return {};
} }
} }

View File

@ -22,6 +22,9 @@
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include <liblangutil/CharStreamProvider.h>
#include <liblangutil/Scanner.h>
#include <sstream> #include <sstream>
#include <regex> #include <regex>
@ -47,42 +50,57 @@ public:
operator std::string() { return m_stream.str(); } operator std::string() { return m_stream.str(); }
}; };
class SourceTextRetriever
{
public:
explicit SourceTextRetriever(langutil::CharStreamProvider const& _charStreamProvider):
m_charStreamProvider(_charStreamProvider) {}
std::string text(langutil::SourceLocation const& _location) const
{
solAssert(_location.hasText(), "");
return std::string{m_charStreamProvider.charStream(*_location.sourceName).text(_location)};
}
protected:
langutil::CharStreamProvider const& m_charStreamProvider;
};
/** /**
* Helper that provides functions which analyze certain source locations * Helper that provides functions which analyze certain source locations
* on a textual base. They utilize regular expression to search for * on a textual base. They utilize regular expression to search for
* keywords or to determine formatting. * keywords or to determine formatting.
*/ */
class SourceAnalysis class SourceAnalysis: public SourceTextRetriever
{ {
public: public:
static bool isMultilineKeyword( using SourceTextRetriever::SourceTextRetriever;
bool isMultilineKeyword(
langutil::SourceLocation const& _location, langutil::SourceLocation const& _location,
std::string const& _keyword std::string const& _keyword
) ) const
{ {
return regex_search( return regex_search(
_location.text(), text(_location),
std::regex{"(\\b" + _keyword + "\\b\\n|\\r|\\r\\n)"} std::regex{"(\\b" + _keyword + "\\b\\n|\\r|\\r\\n)"}
); );
} }
static bool hasMutabilityKeyword(langutil::SourceLocation const& _location) bool hasMutabilityKeyword(langutil::SourceLocation const& _location) const
{ {
return regex_search( return regex_search(
_location.text(), text(_location),
std::regex{"(\\b(pure|view|nonpayable|payable)\\b)"} std::regex{"(\\b(pure|view|nonpayable|payable)\\b)"}
); );
} }
static bool hasVirtualKeyword(langutil::SourceLocation const& _location) bool hasVirtualKeyword(langutil::SourceLocation const& _location) const
{ {
return regex_search(_location.text(), std::regex{"(\\b(virtual)\\b)"}); return regex_search(text(_location), std::regex{"(\\b(virtual)\\b)"});
} }
static bool hasVisibilityKeyword(langutil::SourceLocation const& _location) bool hasVisibilityKeyword(langutil::SourceLocation const& _location) const
{ {
return regex_search(_location.text(), std::regex{"\\bpublic\\b"}); return regex_search(text(_location), std::regex{"\\bpublic\\b"});
} }
}; };
@ -117,21 +135,23 @@ public:
* on a textual base. In general, these utilize regular expressions applied * on a textual base. In general, these utilize regular expressions applied
* to the given source location. * to the given source location.
*/ */
class SourceTransform class SourceTransform: public SourceTextRetriever
{ {
public: public:
using SourceTextRetriever::SourceTextRetriever;
/// Searches for the keyword given and prepends the expression. /// Searches for the keyword given and prepends the expression.
/// E.g. `function f() view;` -> `function f() public view;` /// E.g. `function f() view;` -> `function f() public view;`
static std::string insertBeforeKeyword( std::string insertBeforeKeyword(
langutil::SourceLocation const& _location, langutil::SourceLocation const& _location,
std::string const& _keyword, std::string const& _keyword,
std::string const& _expression std::string const& _expression
) ) const
{ {
auto _regex = std::regex{"(\\b" + _keyword + "\\b)"}; auto _regex = std::regex{"(\\b" + _keyword + "\\b)"};
if (regex_search(_location.text(), _regex)) if (regex_search(text(_location), _regex))
return regex_replace( return regex_replace(
_location.text(), text(_location),
_regex, _regex,
_expression + " " + _keyword, _expression + " " + _keyword,
std::regex_constants::format_first_only std::regex_constants::format_first_only
@ -139,7 +159,7 @@ public:
else else
solAssert( solAssert(
false, false,
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually." "\nNeeds to be fixed manually."
); );
@ -148,22 +168,22 @@ public:
/// Searches for the keyword given and appends the expression. /// Searches for the keyword given and appends the expression.
/// E.g. `function f() public {}` -> `function f() public override {}` /// E.g. `function f() public {}` -> `function f() public override {}`
static std::string insertAfterKeyword( std::string insertAfterKeyword(
langutil::SourceLocation const& _location, langutil::SourceLocation const& _location,
std::string const& _keyword, std::string const& _keyword,
std::string const& _expression std::string const& _expression
) ) const
{ {
bool isMultiline = SourceAnalysis::isMultilineKeyword(_location, _keyword); bool isMultiline = SourceAnalysis{m_charStreamProvider}.isMultilineKeyword(_location, _keyword);
std::string toAppend = isMultiline ? ("\n " + _expression) : (" " + _expression); std::string toAppend = isMultiline ? ("\n " + _expression) : (" " + _expression);
std::regex keyword{"(\\b" + _keyword + "\\b)"}; std::regex keyword{"(\\b" + _keyword + "\\b)"};
if (regex_search(_location.text(), keyword)) if (regex_search(text(_location), keyword))
return regex_replace(_location.text(), keyword, _keyword + toAppend); return regex_replace(text(_location), keyword, _keyword + toAppend);
else else
solAssert( solAssert(
false, false,
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually." "\nNeeds to be fixed manually."
); );
@ -174,22 +194,22 @@ public:
/// Searches for the first right parenthesis and appends the expression /// Searches for the first right parenthesis and appends the expression
/// given. /// given.
/// E.g. `function f() {}` -> `function f() public {}` /// E.g. `function f() {}` -> `function f() public {}`
static std::string insertAfterRightParenthesis( std::string insertAfterRightParenthesis(
langutil::SourceLocation const& _location, langutil::SourceLocation const& _location,
std::string const& _expression std::string const& _expression
) ) const
{ {
auto _regex = std::regex{"(\\))"}; auto _regex = std::regex{"(\\))"};
if (regex_search(_location.text(), _regex)) if (regex_search(text(_location), _regex))
return regex_replace( return regex_replace(
_location.text(), text(_location),
std::regex{"(\\))"}, std::regex{"(\\))"},
") " + _expression ") " + _expression
); );
else else
solAssert( solAssert(
false, false,
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually." "\nNeeds to be fixed manually."
); );
@ -199,38 +219,38 @@ public:
/// Searches for the `function` keyword and its identifier and replaces /// Searches for the `function` keyword and its identifier and replaces
/// both by the expression given. /// both by the expression given.
/// E.g. `function Storage() {}` -> `constructor() {}` /// E.g. `function Storage() {}` -> `constructor() {}`
static std::string replaceFunctionName( std::string replaceFunctionName(
langutil::SourceLocation const& _location, langutil::SourceLocation const& _location,
std::string const& _name, std::string const& _name,
std::string const& _expression std::string const& _expression
) ) const
{ {
auto _regex = std::regex{ "(\\bfunction\\s*" + _name + "\\b)"}; auto _regex = std::regex{ "(\\bfunction\\s*" + _name + "\\b)"};
if (regex_search(_location.text(), _regex)) if (regex_search(text(_location), _regex))
return regex_replace( return regex_replace(
_location.text(), text(_location),
_regex, _regex,
_expression _expression
); );
else else
solAssert( solAssert(
false, false,
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually." "\nNeeds to be fixed manually."
); );
return ""; return "";
} }
static std::string gasUpdate(langutil::SourceLocation const& _location) std::string gasUpdate(langutil::SourceLocation const& _location) const
{ {
// dot, "gas", any number of whitespaces, left bracket // dot, "gas", any number of whitespaces, left bracket
std::regex gasReg{"\\.gas\\s*\\("}; std::regex gasReg{"\\.gas\\s*\\("};
if (regex_search(_location.text(), gasReg)) if (regex_search(text(_location), gasReg))
{ {
std::string out = regex_replace( std::string out = regex_replace(
_location.text(), text(_location),
gasReg, gasReg,
"{gas: ", "{gas: ",
std::regex_constants::format_first_only std::regex_constants::format_first_only
@ -240,22 +260,22 @@ public:
else else
solAssert( solAssert(
false, false,
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually." "\nNeeds to be fixed manually."
); );
return ""; return "";
} }
static std::string valueUpdate(langutil::SourceLocation const& _location) std::string valueUpdate(langutil::SourceLocation const& _location) const
{ {
// dot, "value", any number of whitespaces, left bracket // dot, "value", any number of whitespaces, left bracket
std::regex valueReg{"\\.value\\s*\\("}; std::regex valueReg{"\\.value\\s*\\("};
if (regex_search(_location.text(), valueReg)) if (regex_search(text(_location), valueReg))
{ {
std::string out = regex_replace( std::string out = regex_replace(
_location.text(), text(_location),
valueReg, valueReg,
"{value: ", "{value: ",
std::regex_constants::format_first_only std::regex_constants::format_first_only
@ -265,21 +285,21 @@ public:
else else
solAssert( solAssert(
false, false,
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location << LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually." "\nNeeds to be fixed manually."
); );
return ""; return "";
} }
static std::string nowUpdate(langutil::SourceLocation const& _location) std::string nowUpdate(langutil::SourceLocation const& _location) const
{ {
return regex_replace(_location.text(), std::regex{"now"}, "block.timestamp"); return regex_replace(text(_location), std::regex{"now"}, "block.timestamp");
} }
static std::string removeVisibility(langutil::SourceLocation const& _location) std::string removeVisibility(langutil::SourceLocation const& _location) const
{ {
std::string replacement = _location.text(); std::string replacement = text(_location);
for (auto const& replace: {"public ", "public", "internal ", "internal", "external ", "external"}) for (auto const& replace: {"public ", "public", "internal ", "internal", "external ", "external"})
replacement = regex_replace(replacement, std::regex{replace}, ""); replacement = regex_replace(replacement, std::regex{replace}, "");
return replacement; return replacement;

View File

@ -347,14 +347,14 @@ bool SourceUpgrade::analyzeAndUpgrade(pair<string, string> const& _sourceCode)
log() << "Analyzing and upgrading " << _sourceCode.first << "." << endl; log() << "Analyzing and upgrading " << _sourceCode.first << "." << endl;
if (m_compiler->state() >= CompilerStack::State::AnalysisPerformed) if (m_compiler->state() >= CompilerStack::State::AnalysisPerformed)
m_suite.analyze(m_compiler->ast(_sourceCode.first)); m_suite.analyze(*m_compiler, m_compiler->ast(_sourceCode.first));
if (!m_suite.changes().empty()) if (!m_suite.changes().empty())
{ {
auto& change = m_suite.changes().front(); auto& change = m_suite.changes().front();
if (verbose) if (verbose)
change.log(true); change.log(*m_compiler, true);
if (change.level() == UpgradeChange::Level::Safe) if (change.level() == UpgradeChange::Level::Safe)
{ {
@ -388,20 +388,19 @@ void SourceUpgrade::applyChange(
log() << _change.patch(); log() << _change.patch();
} }
_change.apply(); m_sourceCodes[_sourceCode.first] = _change.apply(_sourceCode.second);
m_sourceCodes[_sourceCode.first] = _change.source();
if (!dryRun) if (!dryRun)
writeInputFile(_sourceCode.first, _change.source()); writeInputFile(_sourceCode.first, m_sourceCodes[_sourceCode.first]);
} }
void SourceUpgrade::printErrors() const void SourceUpgrade::printErrors() const
{ {
auto formatter = make_unique<langutil::SourceReferenceFormatter>(cout, true, false); langutil::SourceReferenceFormatter formatter{cout, *m_compiler, true, false};
for (auto const& error: m_compiler->errors()) for (auto const& error: m_compiler->errors())
if (error->type() != langutil::Error::Type::Warning) if (error->type() != langutil::Error::Type::Warning)
formatter->printErrorInformation(*error); formatter.printErrorInformation(*error);
} }
void SourceUpgrade::printStatistics() const void SourceUpgrade::printStatistics() const

View File

@ -68,29 +68,29 @@ private:
class Suite: public UpgradeSuite class Suite: public UpgradeSuite
{ {
public: public:
void analyze(frontend::SourceUnit const& _sourceUnit) void analyze(langutil::CharStreamProvider const& _charStreamProvider, frontend::SourceUnit const& _sourceUnit)
{ {
/// Solidity 0.5.0 /// Solidity 0.5.0
if (isActivated(Module::ConstructorKeyword)) if (isActivated(Module::ConstructorKeyword))
ConstructorKeyword{m_changes}.analyze(_sourceUnit); ConstructorKeyword{_charStreamProvider, m_changes}.analyze(_sourceUnit);
if (isActivated(Module::VisibilitySpecifier)) if (isActivated(Module::VisibilitySpecifier))
VisibilitySpecifier{m_changes}.analyze(_sourceUnit); VisibilitySpecifier{_charStreamProvider, m_changes}.analyze(_sourceUnit);
/// Solidity 0.6.0 /// Solidity 0.6.0
if (isActivated(Module::AbstractContract)) if (isActivated(Module::AbstractContract))
AbstractContract{m_changes}.analyze(_sourceUnit); AbstractContract{_charStreamProvider, m_changes}.analyze(_sourceUnit);
if (isActivated(Module::OverridingFunction)) if (isActivated(Module::OverridingFunction))
OverridingFunction{m_changes}.analyze(_sourceUnit); OverridingFunction{_charStreamProvider, m_changes}.analyze(_sourceUnit);
if (isActivated(Module::VirtualFunction)) if (isActivated(Module::VirtualFunction))
VirtualFunction{m_changes}.analyze(_sourceUnit); VirtualFunction{_charStreamProvider, m_changes}.analyze(_sourceUnit);
/// Solidity 0.7.0 /// Solidity 0.7.0
if (isActivated(Module::DotSyntax)) if (isActivated(Module::DotSyntax))
DotSyntax{m_changes}.analyze(_sourceUnit); DotSyntax{_charStreamProvider, m_changes}.analyze(_sourceUnit);
if (isActivated(Module::NowKeyword)) if (isActivated(Module::NowKeyword))
NowKeyword{m_changes}.analyze(_sourceUnit); NowKeyword{_charStreamProvider, m_changes}.analyze(_sourceUnit);
if (isActivated(Module::ConstrutorVisibility)) if (isActivated(Module::ConstrutorVisibility))
ConstructorVisibility{m_changes}.analyze(_sourceUnit); ConstructorVisibility{_charStreamProvider, m_changes}.analyze(_sourceUnit);
} }
void activateModule(Module _module) { m_modules.insert(_module); } void activateModule(Module _module) { m_modules.insert(_module); }

View File

@ -36,7 +36,7 @@ void ConstructorKeyword::endVisit(ContractDefinition const& _contract)
m_changes.emplace_back( m_changes.emplace_back(
UpgradeChange::Level::Safe, UpgradeChange::Level::Safe,
function->location(), function->location(),
SourceTransform::replaceFunctionName( SourceTransform{m_charStreamProvider}.replaceFunctionName(
function->location(), function->location(),
function->name(), function->name(),
"constructor" "constructor"
@ -50,6 +50,6 @@ void VisibilitySpecifier::endVisit(FunctionDefinition const& _function)
m_changes.emplace_back( m_changes.emplace_back(
UpgradeChange::Level::Safe, UpgradeChange::Level::Safe,
_function.location(), _function.location(),
SourceTransform::insertAfterRightParenthesis(_function.location(), "public") SourceTransform{m_charStreamProvider}.insertAfterRightParenthesis(_function.location(), "public")
); );
} }

View File

@ -29,75 +29,6 @@ using namespace solidity;
using namespace solidity::frontend; using namespace solidity::frontend;
using namespace solidity::tools; using namespace solidity::tools;
using Contracts = set<ContractDefinition const*, OverrideChecker::CompareByID>;
namespace
{
inline string appendOverride(
FunctionDefinition const& _function,
Contracts const& _expectedContracts
)
{
auto location = _function.location();
string upgradedCode;
string overrideExpression = SourceGeneration::functionOverride(_expectedContracts);
if (SourceAnalysis::hasVirtualKeyword(location))
upgradedCode = SourceTransform::insertAfterKeyword(
location,
"virtual",
overrideExpression
);
else if (SourceAnalysis::hasMutabilityKeyword(location))
upgradedCode = SourceTransform::insertAfterKeyword(
location,
stateMutabilityToString(_function.stateMutability()),
overrideExpression
);
else if (SourceAnalysis::hasVisibilityKeyword(location))
upgradedCode = SourceTransform::insertAfterKeyword(
location,
Declaration::visibilityToString(_function.visibility()),
overrideExpression
);
else
upgradedCode = SourceTransform::insertAfterRightParenthesis(
location,
overrideExpression
);
return upgradedCode;
}
inline string appendVirtual(FunctionDefinition const& _function)
{
auto location = _function.location();
string upgradedCode;
if (SourceAnalysis::hasMutabilityKeyword(location))
upgradedCode = SourceTransform::insertAfterKeyword(
location,
stateMutabilityToString(_function.stateMutability()),
"virtual"
);
else if (SourceAnalysis::hasVisibilityKeyword(location))
upgradedCode = SourceTransform::insertAfterKeyword(
location,
Declaration::visibilityToString(_function.visibility()),
"virtual"
);
else
upgradedCode = SourceTransform::insertAfterRightParenthesis(
_function.location(),
"virtual"
);
return upgradedCode;
}
}
void AbstractContract::endVisit(ContractDefinition const& _contract) void AbstractContract::endVisit(ContractDefinition const& _contract)
{ {
bool isFullyImplemented = _contract.annotation().unimplementedDeclarations->empty(); bool isFullyImplemented = _contract.annotation().unimplementedDeclarations->empty();
@ -110,7 +41,7 @@ void AbstractContract::endVisit(ContractDefinition const& _contract)
m_changes.emplace_back( m_changes.emplace_back(
UpgradeChange::Level::Safe, UpgradeChange::Level::Safe,
_contract.location(), _contract.location(),
SourceTransform::insertBeforeKeyword(_contract.location(), "contract", "abstract") SourceTransform{m_charStreamProvider}.insertBeforeKeyword(_contract.location(), "contract", "abstract")
); );
} }
@ -159,6 +90,42 @@ void OverridingFunction::endVisit(ContractDefinition const& _contract)
} }
} }
string OverridingFunction::appendOverride(
FunctionDefinition const& _function,
Contracts const& _expectedContracts
)
{
auto location = _function.location();
string upgradedCode;
string overrideExpression = SourceGeneration::functionOverride(_expectedContracts);
if (SourceAnalysis{m_charStreamProvider}.hasVirtualKeyword(location))
upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword(
location,
"virtual",
overrideExpression
);
else if (SourceAnalysis{m_charStreamProvider}.hasMutabilityKeyword(location))
upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword(
location,
stateMutabilityToString(_function.stateMutability()),
overrideExpression
);
else if (SourceAnalysis{m_charStreamProvider}.hasVisibilityKeyword(location))
upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword(
location,
Declaration::visibilityToString(_function.visibility()),
overrideExpression
);
else
upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterRightParenthesis(
location,
overrideExpression
);
return upgradedCode;
}
void VirtualFunction::endVisit(ContractDefinition const& _contract) void VirtualFunction::endVisit(ContractDefinition const& _contract)
{ {
auto const& inheritedFunctions = m_overrideChecker.inheritedFunctions(_contract); auto const& inheritedFunctions = m_overrideChecker.inheritedFunctions(_contract);
@ -201,3 +168,30 @@ void VirtualFunction::endVisit(ContractDefinition const& _contract)
} }
} }
} }
string VirtualFunction::appendVirtual(FunctionDefinition const& _function) const
{
auto location = _function.location();
string upgradedCode;
if (SourceAnalysis{m_charStreamProvider}.hasMutabilityKeyword(location))
upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword(
location,
stateMutabilityToString(_function.stateMutability()),
"virtual"
);
else if (SourceAnalysis{m_charStreamProvider}.hasVisibilityKeyword(location))
upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterKeyword(
location,
Declaration::visibilityToString(_function.visibility()),
"virtual"
);
else
upgradedCode = SourceTransform{m_charStreamProvider}.insertAfterRightParenthesis(
_function.location(),
"virtual"
);
return upgradedCode;
}

View File

@ -50,7 +50,14 @@ public:
void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); }
private: private:
using Contracts = std::set<frontend::ContractDefinition const*, frontend::OverrideChecker::CompareByID>;
void endVisit(frontend::ContractDefinition const& _contract) override; void endVisit(frontend::ContractDefinition const& _contract) override;
std::string appendOverride(
frontend::FunctionDefinition const& _function,
Contracts const& _expectedContracts
);
}; };
/** /**
@ -65,6 +72,8 @@ public:
void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); }
private: private:
void endVisit(frontend::ContractDefinition const& _function) override; void endVisit(frontend::ContractDefinition const& _function) override;
std::string appendVirtual(frontend::FunctionDefinition const& _function) const;
}; };
} }

View File

@ -29,14 +29,14 @@ void DotSyntax::endVisit(FunctionCall const& _functionCall)
m_changes.emplace_back( m_changes.emplace_back(
UpgradeChange::Level::Safe, UpgradeChange::Level::Safe,
_functionCall.location(), _functionCall.location(),
SourceTransform::valueUpdate(_functionCall.location()) SourceTransform{m_charStreamProvider}.valueUpdate(_functionCall.location())
); );
if (funcType->gasSet()) if (funcType->gasSet())
m_changes.emplace_back( m_changes.emplace_back(
UpgradeChange::Level::Safe, UpgradeChange::Level::Safe,
_functionCall.location(), _functionCall.location(),
SourceTransform::gasUpdate(_functionCall.location()) SourceTransform{m_charStreamProvider}.gasUpdate(_functionCall.location())
); );
} }
} }
@ -55,7 +55,7 @@ void NowKeyword::endVisit(Identifier const& _identifier)
m_changes.emplace_back( m_changes.emplace_back(
UpgradeChange::Level::Safe, UpgradeChange::Level::Safe,
_identifier.location(), _identifier.location(),
SourceTransform::nowUpdate(_identifier.location()) SourceTransform{m_charStreamProvider}.nowUpdate(_identifier.location())
); );
} }
} }
@ -72,7 +72,7 @@ void ConstructorVisibility::endVisit(ContractDefinition const& _contract)
m_changes.emplace_back( m_changes.emplace_back(
UpgradeChange::Level::Safe, UpgradeChange::Level::Safe,
_contract.location(), _contract.location(),
SourceTransform::insertBeforeKeyword(_contract.location(), "contract", "abstract") SourceTransform{m_charStreamProvider}.insertBeforeKeyword(_contract.location(), "contract", "abstract")
); );
for (FunctionDefinition const* function: _contract.definedFunctions()) for (FunctionDefinition const* function: _contract.definedFunctions())
@ -80,6 +80,6 @@ void ConstructorVisibility::endVisit(ContractDefinition const& _contract)
m_changes.emplace_back( m_changes.emplace_back(
UpgradeChange::Level::Safe, UpgradeChange::Level::Safe,
function->location(), function->location(),
SourceTransform::removeVisibility(function->location()) SourceTransform{m_charStreamProvider}.removeVisibility(function->location())
); );
} }

View File

@ -26,18 +26,20 @@ using namespace solidity::langutil;
using namespace solidity::util; using namespace solidity::util;
using namespace solidity::tools; using namespace solidity::tools;
void UpgradeChange::apply() string UpgradeChange::apply(string _source) const
{ {
m_source.replace( _source.replace(
static_cast<size_t>(m_location.start), static_cast<size_t>(m_location.start),
static_cast<size_t>(m_location.end - m_location.start), m_patch static_cast<size_t>(m_location.end - m_location.start),
m_patch
); );
return _source;
} }
void UpgradeChange::log(bool const _shorten) const void UpgradeChange::log(CharStreamProvider const& _charStreamProvider, bool const _shorten) const
{ {
stringstream os; stringstream os;
SourceReferenceFormatter formatter{os, true, false}; SourceReferenceFormatter formatter{os, _charStreamProvider, true, false};
string start = to_string(m_location.start); string start = to_string(m_location.start);
string end = to_string(m_location.end); string end = to_string(m_location.end);
@ -48,10 +50,10 @@ void UpgradeChange::log(bool const _shorten) const
os << endl; os << endl;
AnsiColorized(os, true, {formatting::BOLD, color}) << "Upgrade change (" << level << ")" << endl; AnsiColorized(os, true, {formatting::BOLD, color}) << "Upgrade change (" << level << ")" << endl;
os << "=======================" << endl; os << "=======================" << endl;
formatter.printSourceLocation(SourceReferenceExtractor::extract(&m_location)); formatter.printSourceLocation(SourceReferenceExtractor::extract(_charStreamProvider, &m_location));
os << endl; os << endl;
LineColumn lineEnd = m_location.source->translatePositionToLineColumn(m_location.end); LineColumn lineEnd = _charStreamProvider.charStream(*m_location.sourceName).translatePositionToLineColumn(m_location.end);
int const leftpad = static_cast<int>(log10(max(lineEnd.line, 1))) + 2; int const leftpad = static_cast<int>(log10(max(lineEnd.line, 1))) + 2;
stringstream output; stringstream output;

View File

@ -19,6 +19,7 @@
#include <libsolutil/AnsiColorized.h> #include <libsolutil/AnsiColorized.h>
#include <liblangutil/SourceLocation.h> #include <liblangutil/SourceLocation.h>
#include <liblangutil/CharStreamProvider.h>
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
@ -47,32 +48,27 @@ public:
Level _level, Level _level,
langutil::SourceLocation _location, langutil::SourceLocation _location,
std::string _patch std::string _patch
) ):
:
m_location(_location), m_location(_location),
m_source(_location.source->source()),
m_patch(std::move(_patch)), m_patch(std::move(_patch)),
m_level(_level) {} m_level(_level)
{}
~UpgradeChange() {} ~UpgradeChange() {}
langutil::SourceLocation const& location() { return m_location; } langutil::SourceLocation const& location() const { return m_location; }
std::string source() const { return m_source; } std::string patch() const { return m_patch; }
std::string patch() { return m_patch; }
Level level() const { return m_level; } Level level() const { return m_level; }
/// Does the actual replacement of code under at current source location. /// Performs the actual replacement on the provided original source code
/// The change is applied on the upgrade-specific copy of source code. /// and returns the modified source code.
/// The altered code is then requested by the upgrade routine later on. std::string apply(std::string _source) const;
void apply(); /// Does a pretty-print of this upgrade change. Since the patch
/// Does a pretty-print of this upgrade change. It uses a source formatter
/// provided by the compiler in order to print affected code. Since the patch
/// can contain a lot of code lines, it can be shortened, which is signaled /// can contain a lot of code lines, it can be shortened, which is signaled
/// by setting the flag. /// by setting the flag.
void log(bool const _shorten = true) const; void log(langutil::CharStreamProvider const& _charStreamProvider, bool const _shorten = true) const;
private: private:
langutil::SourceLocation m_location; langutil::SourceLocation m_location;
std::string m_source;
std::string m_patch; std::string m_patch;
Level m_level; Level m_level;

View File

@ -20,6 +20,7 @@
#include <tools/solidityUpgrade/UpgradeChange.h> #include <tools/solidityUpgrade/UpgradeChange.h>
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
#include <liblangutil/CharStreamProvider.h>
#include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/analysis/OverrideChecker.h> #include <libsolidity/analysis/OverrideChecker.h>
@ -37,13 +38,17 @@ namespace solidity::tools
class Upgrade class Upgrade
{ {
public: public:
Upgrade(std::vector<UpgradeChange>& _changes): m_changes(_changes) {} Upgrade(
langutil::CharStreamProvider const& _charStreamProvider,
std::vector<UpgradeChange>& _changes
): m_changes(_changes), m_charStreamProvider(_charStreamProvider) {}
protected: protected:
/// A reference to a suite-specific set of changes. /// A reference to a suite-specific set of changes.
/// It is passed to all upgrade modules and meant to collect /// It is passed to all upgrade modules and meant to collect
/// reported changes. /// reported changes.
std::vector<UpgradeChange>& m_changes; std::vector<UpgradeChange>& m_changes;
langutil::CharStreamProvider const& m_charStreamProvider;
}; };
/** /**
@ -53,8 +58,11 @@ protected:
class AnalysisUpgrade: public Upgrade, public frontend::ASTConstVisitor class AnalysisUpgrade: public Upgrade, public frontend::ASTConstVisitor
{ {
public: public:
AnalysisUpgrade(std::vector<UpgradeChange>& _changes): AnalysisUpgrade(
Upgrade(_changes), langutil::CharStreamProvider const& _charStreamProvider,
std::vector<UpgradeChange>& _changes
):
Upgrade(_charStreamProvider, _changes),
m_errorReporter(m_errors), m_errorReporter(m_errors),
m_overrideChecker(m_errorReporter) m_overrideChecker(m_errorReporter)
{} {}

View File

@ -27,6 +27,8 @@
#include <tools/yulPhaser/SimulationRNG.h> #include <tools/yulPhaser/SimulationRNG.h>
#include <liblangutil/CharStream.h> #include <liblangutil/CharStream.h>
#include <liblangutil/SourceReferenceFormatter.h>
#include <liblangutil/Scanner.h>
#include <libsolutil/Assertions.h> #include <libsolutil/Assertions.h>
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
@ -389,7 +391,9 @@ vector<Program> ProgramFactory::build(Options const& _options)
variant<Program, ErrorList> programOrErrors = Program::load(sourceCode); variant<Program, ErrorList> programOrErrors = Program::load(sourceCode);
if (holds_alternative<ErrorList>(programOrErrors)) if (holds_alternative<ErrorList>(programOrErrors))
{ {
cerr << get<ErrorList>(programOrErrors) << endl; SourceReferenceFormatter{cerr, SingletonCharStreamProvider(sourceCode), true, false}
.printErrorInformation(get<ErrorList>(programOrErrors));
cerr << endl;
assertThrow(false, InvalidProgram, "Failed to load program " + path); assertThrow(false, InvalidProgram, "Failed to load program " + path);
} }

View File

@ -60,16 +60,6 @@ ostream& operator<<(ostream& _stream, Program const& _program);
} }
ostream& std::operator<<(ostream& _outputStream, ErrorList const& _errors)
{
SourceReferenceFormatter formatter(_outputStream, true, false);
for (auto const& error: _errors)
formatter.printErrorInformation(*error);
return _outputStream;
}
Program::Program(Program const& program): Program::Program(Program const& program):
m_ast(make_unique<Block>(get<Block>(ASTCopier{}(*program.m_ast)))), m_ast(make_unique<Block>(get<Block>(ASTCopier{}(*program.m_ast)))),
m_dialect{program.m_dialect}, m_dialect{program.m_dialect},

View File

@ -35,6 +35,7 @@ namespace solidity::langutil
{ {
class CharStream; class CharStream;
class Scanner;
} }
@ -47,13 +48,6 @@ struct CodeWeights;
} }
namespace std
{
std::ostream& operator<<(std::ostream& _outputStream, solidity::langutil::ErrorList const& _errors);
}
namespace solidity::phaser namespace solidity::phaser
{ {