Merge pull request #11596 from ethereum/simplifySourceLocation

Remove CharStream from SourceLocation.
This commit is contained in:
chriseth 2021-07-14 15:50:55 +02:00 committed by GitHub
commit 41e06eab4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
84 changed files with 690 additions and 601 deletions

View File

@ -76,15 +76,15 @@ namespace
string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location)
{
if (!_location.hasText() || _sourceCodes.empty())
return "";
return {};
auto it = _sourceCodes.find(_location.source->name());
auto it = _sourceCodes.find(*_location.sourceName);
if (it == _sourceCodes.end())
return "";
return {};
string const& source = it->second;
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));
auto newLinePos = cut.find_first_of("\n");
@ -152,8 +152,8 @@ public:
if (!m_location.isValid())
return;
m_out << m_prefix << " /*";
if (m_location.source)
m_out << " \"" + m_location.source->name() + "\"";
if (m_location.sourceName)
m_out << " " + escapeAndQuoteYulString(*m_location.sourceName);
if (m_location.hasText())
m_out << ":" << to_string(m_location.start) + ":" + to_string(m_location.end);
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)
{
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())
sourceIndex = static_cast<int>(iter->second);
}

View File

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

View File

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

View File

@ -45,9 +45,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity scanner.
* Character stream / input file.
*/
#include <liblangutil/CharStream.h>
@ -118,3 +116,15 @@ tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const
}
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.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity scanner.
* Character stream / input file.
*/
#pragma once
@ -60,6 +58,8 @@
namespace solidity::langutil
{
struct SourceLocation;
/**
* Bidirectional stream of characters.
*
@ -69,8 +69,8 @@ class CharStream
{
public:
CharStream() = default;
explicit CharStream(std::string _source, std::string name):
m_source(std::move(_source)), m_name(std::move(name)) {}
CharStream(std::string _source, std::string _name):
m_source(std::move(_source)), m_name(std::move(_name)) {}
size_t position() const { return m_position; }
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& name() const noexcept { return m_name; }
size_t size() const { return m_source.size(); }
///@{
///@name Error printing helper functions
/// Functions that help pretty-printing parse errors
@ -112,6 +114,10 @@ public:
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:
std::string m_source;
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)
{
m_source = make_shared<CharStream>(std::move(_source));
m_sourceName = make_shared<string>(m_source->name());
reset();
}
@ -145,6 +146,7 @@ void Scanner::reset(shared_ptr<CharStream> _source)
{
solAssert(_source.get() != nullptr, "You MUST provide a CharStream when resetting.");
m_source = std::move(_source);
m_sourceName = make_shared<string>(m_source->name());
reset();
}
@ -497,7 +499,7 @@ Token Scanner::scanSlash()
return skipSingleLineComment();
// doxygen style /// comment
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].location.end = static_cast<int>(scanSingleLineDocComment());
return Token::Whitespace;
@ -526,7 +528,7 @@ Token Scanner::scanSlash()
return skipMultiLineComment();
// we actually have a multiline documentation comment
m_skippedComments[NextNext].location.start = firstSlashPosition;
m_skippedComments[NextNext].location.source = m_source;
m_skippedComments[NextNext].location.sourceName = m_sourceName;
Token comment = scanMultiLineDocComment();
m_skippedComments[NextNext].location.end = static_cast<int>(sourcePos());
m_skippedComments[NextNext].token = comment;
@ -766,7 +768,7 @@ void Scanner::scanToken()
}
while (token == Token::Whitespace);
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].extendedTokenInfo = make_tuple(m, n);
}

View File

@ -177,14 +177,6 @@ public:
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:
inline Token setError(ScannerError _error) noexcept
@ -270,6 +262,7 @@ private:
TokenDesc m_tokens[3] = {}; // desc for the current, next and nextnext token
std::shared_ptr<CharStream> m_source;
std::shared_ptr<std::string const> m_sourceName;
ScannerKind m_kind = ScannerKind::Solidity;

View File

@ -22,15 +22,15 @@
#include <boost/algorithm/string.hpp>
using namespace solidity;
namespace solidity::langutil
{
using namespace solidity::langutil;
using namespace std;
SourceLocation const parseSourceLocation(std::string const& _input, std::string const& _sourceName, size_t _maxIndex)
SourceLocation solidity::langutil::parseSourceLocation(string const& _input, vector<shared_ptr<string const>> const& _sourceNames)
{
// Expected input: "start:length:sourceindex"
enum SrcElem : size_t { Start, Length, Index };
enum SrcElem: size_t { Start, Length, Index };
std::vector<std::string> pos;
vector<string> pos;
boost::algorithm::split(pos, _input, boost::is_any_of(":"));
@ -38,19 +38,15 @@ SourceLocation const parseSourceLocation(std::string const& _input, std::string
auto const sourceIndex = stoi(pos[Index]);
astAssert(
sourceIndex == -1 || _maxIndex >= static_cast<size_t>(sourceIndex),
sourceIndex == -1 || (0 <= sourceIndex && static_cast<size_t>(sourceIndex) < _sourceNames.size()),
"'src'-field ill-formatted or src-index too high"
);
int start = stoi(pos[Start]);
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
std::shared_ptr<langutil::CharStream> source;
SourceLocation result{start, end, {}};
if (sourceIndex != -1)
source = std::make_shared<langutil::CharStream>("", _sourceName);
return SourceLocation{start, end, source};
}
result.sourceName = _sourceNames.at(static_cast<size_t>(sourceIndex));
return result;
}

View File

@ -26,8 +26,6 @@
#include <libsolutil/Assertions.h>
#include <libsolutil/Exceptions.h>
#include <liblangutil/CharStream.h>
#include <limits>
#include <memory>
#include <string>
@ -44,51 +42,44 @@ struct SourceLocation
{
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); }
inline bool operator<(SourceLocation const& _other) const
bool operator<(SourceLocation const& _other) const
{
if (!source|| !_other.source)
return std::make_tuple(int(!!source), start, end) < std::make_tuple(int(!!_other.source), _other.start, _other.end);
if (!sourceName || !_other.sourceName)
return std::make_tuple(int(!!sourceName), start, end) < std::make_tuple(int(!!_other.sourceName), _other.start, _other.end);
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 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 _other.start < end && start < _other.end;
}
bool isValid() const { return source || start != -1 || end != -1; }
bool hasText() const
bool equalSources(SourceLocation const& _other) const
{
return
source &&
0 <= start &&
start <= end &&
end <= int(source->source().length());
if (!!sourceName != !!_other.sourceName)
return false;
if (sourceName && *sourceName != *_other.sourceName)
return false;
return true;
}
std::string text() const
{
assertThrow(source, SourceLocationError, "Requested text from null source.");
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));
}
bool isValid() const { return sourceName || start != -1 || end != -1; }
bool hasText() const { return sourceName && 0 <= start && start <= end; }
/// @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
@ -97,8 +88,8 @@ struct SourceLocation
/// @param _b, then start resp. end of the result will be -1 as well).
static SourceLocation smallestCovering(SourceLocation _a, SourceLocation const& _b)
{
if (!_a.source)
_a.source = _b.source;
if (!_a.sourceName)
_a.sourceName = _b.sourceName;
if (_a.start < 0)
_a.start = _b.start;
@ -112,13 +103,12 @@ struct SourceLocation
int start = -1;
int end = -1;
std::shared_ptr<CharStream> source;
std::shared_ptr<std::string const> sourceName;
};
SourceLocation const parseSourceLocation(
SourceLocation parseSourceLocation(
std::string const& _input,
std::string const& _sourceName,
size_t _maxIndex = std::numeric_limits<size_t>::max()
std::vector<std::shared_ptr<std::string const>> const& _sourceNames
);
/// Stream output for Location (used e.g. in boost exceptions).
@ -127,8 +117,8 @@ inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _locat
if (!_location.isValid())
return _out << "NO_LOCATION_SPECIFIED";
if (_location.source)
_out << _location.source->name();
if (_location.sourceName)
_out << *_location.sourceName;
_out << "[" << _location.start << "," << _location.end << "]";

View File

@ -16,8 +16,9 @@
*/
// SPDX-License-Identifier: GPL-3.0
#include <liblangutil/SourceReferenceExtractor.h>
#include <liblangutil/CharStream.h>
#include <liblangutil/Exceptions.h>
#include <liblangutil/CharStreamProvider.h>
#include <liblangutil/CharStream.h>
#include <algorithm>
#include <cmath>
@ -26,46 +27,57 @@ using namespace std;
using namespace solidity;
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);
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;
auto secondaryLocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
if (secondaryLocation && !secondaryLocation->infos.empty())
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};
}
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";
Message message = extract(_error, category);
Message message = extract(_charStreamProvider, _error, category);
message.errorId = _error.errorId();
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));
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 end = source->translatePositionToLineColumn(_location->end);
LineColumn end = charStream.translatePositionToLineColumn(_location->end);
bool const isMultiline = start.line != end.line;
string line = source->lineAtPosition(_location->start);
string line = charStream.lineAtPosition(_location->start);
int locationLength =
isMultiline ?
@ -102,7 +114,7 @@ SourceReference SourceReferenceExtractor::extract(SourceLocation const* _locatio
return SourceReference{
std::move(message),
source->name(),
*_location->sourceName,
interest,
isMultiline,
line,

View File

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

View File

@ -23,6 +23,7 @@
#include <liblangutil/Exceptions.h>
#include <liblangutil/SourceReferenceExtractor.h>
#include <liblangutil/CharStreamProvider.h>
#include <libsolutil/AnsiColorized.h>
@ -37,34 +38,53 @@ struct SourceLocation;
class SourceReferenceFormatter
{
public:
SourceReferenceFormatter(std::ostream& _stream, bool _colored, bool _withErrorIds):
m_stream(_stream), m_colored(_colored), m_withErrorIds(_withErrorIds)
SourceReferenceFormatter(
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.
void printSourceLocation(SourceReference const& _ref);
void printExceptionInformation(SourceReferenceExtractor::Message const& _msg);
void printExceptionInformation(util::Exception const& _exception, std::string const& _category);
void printErrorInformation(langutil::ErrorList const& _errors);
void printErrorInformation(Error const& _error);
static std::string formatExceptionInformation(
util::Exception const& _exception,
std::string const& _name,
CharStreamProvider const& _charStreamProvider,
bool _colored = false,
bool _withErrorIds = false
)
{
std::ostringstream errorOutput;
SourceReferenceFormatter formatter(errorOutput, _colored, _withErrorIds);
SourceReferenceFormatter formatter(errorOutput, _charStreamProvider, _colored, _withErrorIds);
formatter.printExceptionInformation(_exception, _name);
return errorOutput.str();
}
static std::string formatErrorInformation(Error const& _error)
static std::string formatErrorInformation(
Error const& _error,
CharStreamProvider const& _charStreamProvider
)
{
return formatExceptionInformation(
_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:
std::ostream& m_stream;
CharStreamProvider const& m_charStreamProvider;
bool m_colored;
bool m_withErrorIds;
};

View File

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

View File

@ -68,7 +68,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit)
string(";\"");
// 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())
m_sourceUnit->annotation().useABICoderV2 = true;

View File

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

View File

@ -57,14 +57,12 @@ ASTPointer<T> ASTJsonImporter::nullOrCast(Json::Value const& _json)
map<string, ASTPointer<SourceUnit>> ASTJsonImporter::jsonToSourceUnit(map<string, Json::Value> const& _sourceList)
{
m_sourceList = _sourceList;
for (auto const& src: _sourceList)
m_sourceLocations.emplace_back(make_shared<string const>(src.first));
for (auto const& srcPair: m_sourceList)
m_sourceNames.emplace_back(make_shared<string const>(src.first));
for (auto const& srcPair: _sourceList)
{
astAssert(!srcPair.second.isNull(), "");
astAssert(member(srcPair.second,"nodeType") == "SourceUnit", "The 'nodeType' of the highest node must be 'SourceUnit'.");
m_currentSourceName = srcPair.first;
m_sourceUnits[srcPair.first] = createSourceUnit(srcPair.second, srcPair.first);
}
return m_sourceUnits;
@ -94,14 +92,14 @@ SourceLocation const ASTJsonImporter::createSourceLocation(Json::Value const& _n
{
astAssert(member(_node, "src").isString(), "'src' must be a string");
return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_currentSourceName, m_sourceLocations.size());
return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceNames);
}
SourceLocation ASTJsonImporter::createNameSourceLocation(Json::Value const& _node)
{
astAssert(member(_node, "nameLocation").isString(), "'nameLocation' must be a string");
return solidity::langutil::parseSourceLocation(_node["nameLocation"].asString(), m_currentSourceName, m_sourceLocations.size());
return solidity::langutil::parseSourceLocation(_node["nameLocation"].asString(), m_sourceNames);
}
template<class T>
@ -616,7 +614,7 @@ ASTPointer<InlineAssembly> ASTJsonImporter::createInlineAssembly(Json::Value con
astAssert(m_evmVersion == evmVersion, "Imported tree evm version differs from configured evm version!");
yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion.value());
shared_ptr<yul::Block> operations = make_shared<yul::Block>(yul::AsmJsonImporter(m_currentSourceName).createBlock(member(_node, "AST")));
shared_ptr<yul::Block> operations = make_shared<yul::Block>(yul::AsmJsonImporter(m_sourceNames).createBlock(member(_node, "AST")));
return createASTNode<InlineAssembly>(
_node,
nullOrASTString(_node, "documentation"),

View File

@ -152,13 +152,10 @@ private:
///@}
// =========== member variables ===============
/// Stores filepath as sourcenames to AST in JSON format
std::map<std::string, Json::Value> m_sourceList;
/// list of filepaths (used as sourcenames)
std::vector<std::shared_ptr<std::string const>> m_sourceLocations;
/// list of source names, order by source index
std::vector<std::shared_ptr<std::string const>> m_sourceNames;
/// filepath to AST
std::map<std::string, ASTPointer<SourceUnit>> m_sourceUnits;
std::string m_currentSourceName;
/// IDs already used by the nodes
std::set<int64_t> m_usedIDs;
/// Configured EVM version

View File

@ -455,7 +455,9 @@ void CompilerContext::appendInlineAssembly(
_assembly + "\n"
"------------------ Errors: ----------------\n";
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";
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)
{
solAssert(_location.sourceName, "");
return "/// @src "
+ to_string(_context.sourceIndices().at(_location.source->name()))
+ to_string(_context.sourceIndices().at(*_location.sourceName))
+ ":"
+ to_string(_location.start)
+ ","

View File

@ -101,7 +101,10 @@ pair<string, string> IRGenerator::run(
{
string errorMessage;
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");
}
asmStack.optimize();

View File

@ -22,6 +22,9 @@
#include <libsmtutil/SMTPortfolio.h>
#include <liblangutil/CharStream.h>
#include <liblangutil/CharStreamProvider.h>
#ifdef HAVE_Z3_DLOPEN
#include <z3_version.h>
#endif
@ -38,9 +41,10 @@ BMC::BMC(
map<h256, string> const& _smtlib2Responses,
ReadCallback::Callback const& _smtCallback,
smtutil::SMTSolverChoice _enabledSolvers,
ModelCheckerSettings const& _settings
ModelCheckerSettings const& _settings,
CharStreamProvider const& _charStreamProvider
):
SMTEncoder(_context, _settings),
SMTEncoder(_context, _settings, _charStreamProvider),
m_interface(make_unique<smtutil::SMTPortfolio>(_smtlib2Responses, _smtCallback, _enabledSolvers, _settings.timeout)),
m_outerErrorReporter(_errorReporter)
{
@ -650,7 +654,12 @@ pair<vector<smtutil::Expression>, vector<string>> BMC::modelExpressions()
if (uf->annotation().type->isValueType())
{
expressionsToEvaluate.emplace_back(expr(*uf));
expressionNames.push_back(uf->location().text());
string expressionName;
if (uf->location().hasText())
expressionName = m_charStreamProvider.charStream(*uf->location().sourceName).text(
uf->location()
);
expressionNames.push_back(move(expressionName));
}
return {expressionsToEvaluate, expressionNames};

View File

@ -63,7 +63,8 @@ public:
std::map<h256, std::string> const& _smtlib2Responses,
ReadCallback::Callback const& _smtCallback,
smtutil::SMTSolverChoice _enabledSolvers,
ModelCheckerSettings const& _settings
ModelCheckerSettings const& _settings,
langutil::CharStreamProvider const& _charStreamProvider
);
void analyze(SourceUnit const& _sources, std::map<ASTNode const*, std::set<VerificationTargetType>> _solvedTargets);

View File

@ -57,9 +57,10 @@ CHC::CHC(
[[maybe_unused]] map<util::h256, string> const& _smtlib2Responses,
[[maybe_unused]] ReadCallback::Callback const& _smtCallback,
SMTSolverChoice _enabledSolvers,
ModelCheckerSettings const& _settings
ModelCheckerSettings const& _settings,
CharStreamProvider const& _charStreamProvider
):
SMTEncoder(_context, _settings),
SMTEncoder(_context, _settings, _charStreamProvider),
m_outerErrorReporter(_errorReporter),
m_enabledSolvers(_enabledSolvers)
{
@ -1741,7 +1742,7 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
path.emplace_back("State: " + modelMsg);
}
string txCex = summaryPredicate->formatSummaryCall(summaryArgs);
string txCex = summaryPredicate->formatSummaryCall(summaryArgs, m_charStreamProvider);
list<string> calls;
auto dfs = [&](unsigned parent, unsigned node, unsigned depth, auto&& _dfs) -> void {
@ -1753,7 +1754,7 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
if (!pred->isConstructorSummary())
for (unsigned v: callGraph[node])
_dfs(node, v, depth + 1, _dfs);
calls.push_front(string(depth * 4, ' ') + pred->formatSummaryCall(nodeArgs(node)));
calls.push_front(string(depth * 4, ' ') + pred->formatSummaryCall(nodeArgs(node), m_charStreamProvider));
if (pred->isInternalCall())
calls.front() += " -- internal call";
else if (pred->isExternalCallTrusted())

View File

@ -57,7 +57,8 @@ public:
std::map<util::h256, std::string> const& _smtlib2Responses,
ReadCallback::Callback const& _smtCallback,
smtutil::SMTSolverChoice _enabledSolvers,
ModelCheckerSettings const& _settings
ModelCheckerSettings const& _settings,
langutil::CharStreamProvider const& _charStreamProvider
);
void analyze(SourceUnit const& _sources);

View File

@ -32,6 +32,7 @@ using namespace solidity::frontend;
ModelChecker::ModelChecker(
ErrorReporter& _errorReporter,
langutil::CharStreamProvider const& _charStreamProvider,
map<h256, string> const& _smtlib2Responses,
ModelCheckerSettings _settings,
ReadCallback::Callback const& _smtCallback,
@ -40,8 +41,8 @@ ModelChecker::ModelChecker(
m_errorReporter(_errorReporter),
m_settings(_settings),
m_context(),
m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings),
m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings)
m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings, _charStreamProvider),
m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, m_settings, _charStreamProvider)
{
}

View File

@ -49,6 +49,7 @@ public:
/// should be used, even if all are available. The default choice is to use all.
ModelChecker(
langutil::ErrorReporter& _errorReporter,
langutil::CharStreamProvider const& _charStreamProvider,
std::map<solidity::util::h256, std::string> const& _smtlib2Responses,
ModelCheckerSettings _settings = ModelCheckerSettings{},
ReadCallback::Callback const& _smtCallback = ReadCallback::Callback(),

View File

@ -20,6 +20,8 @@
#include <libsolidity/formal/SMTEncoder.h>
#include <liblangutil/CharStreamProvider.h>
#include <liblangutil/CharStream.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/TypeProvider.h>
@ -196,12 +198,20 @@ bool Predicate::isInterface() const
return m_type == PredicateType::Interface;
}
string Predicate::formatSummaryCall(vector<smtutil::Expression> const& _args) const
string Predicate::formatSummaryCall(
vector<smtutil::Expression> const& _args,
langutil::CharStreamProvider const& _charStreamProvider
) const
{
solAssert(isSummary(), "");
if (auto funCall = programFunctionCall())
return funCall->location().text();
{
if (funCall->location().hasText())
return string(_charStreamProvider.charStream(*funCall->location().sourceName).text(funCall->location()));
else
return {};
}
/// 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,

View File

@ -27,6 +27,11 @@
#include <optional>
#include <vector>
namespace solidity::langutil
{
class CharStreamProvider;
}
namespace solidity::frontend
{
@ -142,7 +147,10 @@ public:
/// @returns a formatted string representing a call to this predicate
/// with _args.
std::string formatSummaryCall(std::vector<smtutil::Expression> const& _args) const;
std::string formatSummaryCall(
std::vector<smtutil::Expression> const& _args,
langutil::CharStreamProvider const& _charStreamProvider
) const;
/// @returns the values of the state variables from _args at the point
/// where this summary was reached.

View File

@ -30,6 +30,8 @@
#include <libsmtutil/SMTPortfolio.h>
#include <libsmtutil/Helpers.h>
#include <liblangutil/CharStreamProvider.h>
#include <range/v3/view.hpp>
#include <boost/range/adaptors.hpp>
@ -45,11 +47,13 @@ using namespace solidity::frontend;
SMTEncoder::SMTEncoder(
smt::EncodingContext& _context,
ModelCheckerSettings const& _settings
ModelCheckerSettings const& _settings,
langutil::CharStreamProvider const& _charStreamProvider
):
m_errorReporter(m_smtErrors),
m_context(_context),
m_settings(_settings)
m_settings(_settings),
m_charStreamProvider(_charStreamProvider)
{
}

View File

@ -43,6 +43,7 @@ namespace solidity::langutil
{
class ErrorReporter;
struct SourceLocation;
class CharStreamProvider;
}
namespace solidity::frontend
@ -53,7 +54,8 @@ class SMTEncoder: public ASTConstVisitor
public:
SMTEncoder(
smt::EncodingContext& _context,
ModelCheckerSettings const& _settings
ModelCheckerSettings const& _settings,
langutil::CharStreamProvider const& _charStreamProvider
);
/// @returns true if engine should proceed with analysis.
@ -469,6 +471,10 @@ protected:
ModelCheckerSettings const& m_settings;
/// Character stream for each source,
/// used for retrieving source text of expressions for e.g. counter-examples.
langutil::CharStreamProvider const& m_charStreamProvider;
smt::SymbolicState& state();
};

View File

@ -555,7 +555,7 @@ bool CompilerStack::analyze()
if (noErrors)
{
ModelChecker modelChecker(m_errorReporter, m_smtlib2Responses, m_modelCheckerSettings, m_readFile, m_enabledSMTSolvers);
ModelChecker modelChecker(m_errorReporter, *this, m_smtlib2Responses, m_modelCheckerSettings, m_readFile, m_enabledSMTSolvers);
auto allSources = applyMap(m_sourceOrder, [](Source const* _source) { return _source->ast; });
modelChecker.enableAllEnginesIfPragmaPresent(allSources);
modelChecker.checkRequestedSourcesAndContracts(allSources);
@ -926,19 +926,6 @@ map<string, unsigned> CompilerStack::sourceIndices() const
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
{
if (m_stackState < AnalysisPerformed)
@ -1048,12 +1035,15 @@ string const& CompilerStack::metadata(Contract const& _contract) const
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)
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
@ -1095,19 +1085,6 @@ size_t CompilerStack::functionEntryPoint(
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
{
if (keccak256HashCached == h256{})

View File

@ -38,6 +38,7 @@
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/EVMVersion.h>
#include <liblangutil/SourceLocation.h>
#include <liblangutil/CharStreamProvider.h>
#include <libevmasm/LinkerObject.h>
@ -57,6 +58,7 @@
namespace solidity::langutil
{
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
* there are errors. In any case, producing code is only possible without errors.
*/
class CompilerStack
class CompilerStack: public langutil::CharStreamProvider
{
public:
/// Noncopyable.
@ -120,7 +122,7 @@ public:
/// and must not emit exceptions.
explicit CompilerStack(ReadCallback::Callback _readFile = ReadCallback::Callback());
~CompilerStack();
~CompilerStack() override;
/// @returns the list of errors that occurred during parsing and type checking.
langutil::ErrorList const& errors() const { return m_errorReporter.errors(); }
@ -239,12 +241,8 @@ public:
/// by sourceNames().
std::map<std::string, unsigned> sourceIndices() const;
/// @returns the reverse mapping of source indices to their respective
/// CharStream instances.
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 previously used character stream, useful for counting lines during error reporting.
langutil::CharStream const& charStream(std::string const& _sourceName) const override;
/// @returns the parsed source unit with the supplied name.
SourceUnit const& ast(std::string const& _sourceName) const;
@ -253,11 +251,6 @@ public:
/// does not exist.
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
/// by calling @a addSMTLib2Response).
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 sourceLocation;
if (location && location->source)
if (location && location->sourceName)
{
sourceLocation["file"] = location->source->name();
sourceLocation["file"] = *location->sourceName;
sourceLocation["start"] = location->start;
sourceLocation["end"] = location->end;
}
@ -109,6 +109,7 @@ Json::Value formatSecondarySourceLocation(SecondarySourceLocation const* _second
}
Json::Value formatErrorWithException(
CharStreamProvider const& _charStreamProvider,
util::Exception const& _exception,
bool const& _warning,
string const& _type,
@ -119,7 +120,11 @@ Json::Value formatErrorWithException(
{
string message;
// 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))
message = ((_message.length() > 0) ? (_message + ":") : "") + *description;
@ -1017,6 +1022,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
Error const& err = dynamic_cast<Error const&>(*error);
errors.append(formatErrorWithException(
compilerStack,
*error,
err.type() == Error::Type::Warning,
err.typeName(),
@ -1030,6 +1036,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
catch (Error const& _error)
{
errors.append(formatErrorWithException(
compilerStack,
_error,
false,
_error.typeName(),
@ -1050,6 +1057,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
catch (CompilerError const& _exception)
{
errors.append(formatErrorWithException(
compilerStack,
_exception,
false,
"CompilerError",
@ -1060,6 +1068,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
catch (InternalCompilerError const& _exception)
{
errors.append(formatErrorWithException(
compilerStack,
_exception,
false,
"InternalCompilerError",
@ -1070,6 +1079,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
catch (UnimplementedFeatureError const& _exception)
{
errors.append(formatErrorWithException(
compilerStack,
_exception,
false,
"UnimplementedFeatureError",
@ -1080,6 +1090,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
catch (yul::YulException const& _exception)
{
errors.append(formatErrorWithException(
compilerStack,
_exception,
false,
"YulException",
@ -1090,6 +1101,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
catch (smtutil::SMTLogicError const& _exception)
{
errors.append(formatErrorWithException(
compilerStack,
_exception,
false,
"SMTLogicException",
@ -1297,6 +1309,7 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
auto err = dynamic_pointer_cast<Error const>(error);
errors.append(formatErrorWithException(
stack,
*error,
err->type() == Error::Type::Warning,
err->typeName(),

View File

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

View File

@ -45,7 +45,7 @@ SourceLocation const AsmJsonImporter::createSourceLocation(Json::Value const& _n
{
yulAssert(member(_node, "src").isString(), "'src' must be a string");
return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceName);
return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceNames);
}
template <class T>
@ -53,10 +53,7 @@ T AsmJsonImporter::createAsmNode(Json::Value const& _node)
{
T r;
SourceLocation location = createSourceLocation(_node);
yulAssert(
location.source && 0 <= location.start && location.start <= location.end,
"Invalid source location in Asm AST"
);
yulAssert(location.hasText(), "Invalid source location in Asm AST");
r.debugData = DebugData::create(location);
return r;
}

View File

@ -38,7 +38,9 @@ namespace solidity::yul
class AsmJsonImporter
{
public:
explicit AsmJsonImporter(std::string _sourceName) : m_sourceName(std::move(_sourceName)) {}
explicit AsmJsonImporter(std::vector<std::shared_ptr<std::string const>> const& _sourceNames):
m_sourceNames(_sourceNames)
{}
yul::Block createBlock(Json::Value const& _node);
private:
@ -70,8 +72,7 @@ private:
yul::Break createBreak(Json::Value const& _node);
yul::Continue createContinue(Json::Value const& _node);
std::string m_sourceName;
std::vector<std::shared_ptr<std::string const>> const& m_sourceNames;
};
}

View File

@ -80,7 +80,7 @@ unique_ptr<Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner, bool _
try
{
m_scanner = _scanner;
if (m_charStreamMap)
if (m_sourceNames)
fetchSourceLocationFromComment();
auto block = make_unique<Block>(parseBlock());
if (!_reuseScanner)
@ -105,7 +105,7 @@ langutil::Token Parser::advance()
void Parser::fetchSourceLocationFromComment()
{
solAssert(m_charStreamMap.has_value(), "");
solAssert(m_sourceNames.has_value(), "");
if (m_scanner->currentCommentLiteral().empty())
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.");
else if (sourceIndex == -1)
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.");
else
{
shared_ptr<CharStream> charStream = m_charStreamMap->at(static_cast<unsigned>(*sourceIndex));
solAssert(charStream, "");
m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, charStream});
shared_ptr<string const> sourceName = m_sourceNames->at(static_cast<unsigned>(*sourceIndex));
solAssert(sourceName, "");
m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, move(sourceName)});
}
}
}

View File

@ -72,11 +72,11 @@ public:
explicit Parser(
langutil::ErrorReporter& _errorReporter,
Dialect const& _dialect,
std::map<unsigned, std::shared_ptr<langutil::CharStream>> _charStreamMap
std::map<unsigned, std::shared_ptr<std::string const>> _sourceNames
):
ParserBase(_errorReporter),
m_dialect(_dialect),
m_charStreamMap{std::move(_charStreamMap)},
m_sourceNames{std::move(_sourceNames)},
m_debugDataOverride{DebugData::create()},
m_useSourceLocationFrom{UseSourceLocationFrom::Comments}
{}
@ -144,7 +144,7 @@ protected:
private:
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;
std::shared_ptr<DebugData const> m_debugDataOverride;
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, "");
return *m_scanner;
yulAssert(m_charStream, "");
yulAssert(m_charStream->name() == _sourceName, "");
return *m_charStream;
}
bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source)
{
m_errors.clear();
m_analysisSuccessful = false;
m_scanner = make_shared<Scanner>(CharStream(_source, _sourceName));
m_parserResult = ObjectParser(m_errorReporter, languageToDialect(m_language, m_evmVersion)).parse(m_scanner, false);
m_charStream = make_unique<CharStream>(_source, _sourceName);
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())
return false;
yulAssert(m_parserResult, "");
@ -132,7 +134,8 @@ void AssemblyStack::translate(AssemblyStack::Language _targetLanguage)
);
*m_parserResult = EVMToEwasmTranslator(
languageToDialect(m_language, m_evmVersion)
languageToDialect(m_language, m_evmVersion),
*this
).run(*parserResult());
m_language = _targetLanguage;
@ -241,6 +244,7 @@ AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
{
auto [creationAssembly, deployedAssembly] = assembleEVMWithDeployed(_deployName);
yulAssert(creationAssembly, "");
yulAssert(m_charStream, "");
MachineAssemblyObject creationObject;
creationObject.bytecode = make_shared<evmasm::LinkerObject>(creationAssembly->assemble());
@ -249,7 +253,7 @@ AssemblyStack::assembleWithDeployed(optional<string_view> _deployName) const
creationObject.sourceMappings = make_unique<string>(
evmasm::AssemblyItem::computeSourceMapping(
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>(
evmasm::AssemblyItem::computeSourceMapping(
deployedAssembly->items(),
{{scanner().charStream() ? scanner().charStream()->name() : "", 0}}
{{m_charStream->name(), 0}}
)
);
}

View File

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

View File

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

View File

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

View File

@ -561,7 +561,7 @@ bool CommandLineInterface::compile()
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
{
@ -598,8 +598,7 @@ bool CommandLineInterface::compile()
if (!m_compiler->analyze())
{
for (auto const& error: m_compiler->errors())
formatter.printErrorInformation(*error);
formatter.printErrorInformation(m_compiler->errors());
astAssert(false, "Analysis of the AST failed");
}
}
@ -974,7 +973,7 @@ bool CommandLineInterface::assemble(
for (auto const& sourceAndStack: assemblyStacks)
{
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())
{

View File

@ -57,11 +57,11 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
{ "sub.asm", 1 }
};
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 _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});
// PushImmutable
_subAsm.appendImmutable("someImmutable");
@ -172,11 +172,11 @@ BOOST_AUTO_TEST_CASE(immutable)
{ "sub.asm", 1 }
};
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 _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.appendImmutable("someImmutable");
_subAsm.appendImmutable("someOtherImmutable");

View File

@ -34,9 +34,9 @@ BOOST_AUTO_TEST_SUITE(SourceLocationTest)
BOOST_AUTO_TEST_CASE(test_fail)
{
auto const source = std::make_shared<CharStream>("lorem ipsum", "source");
auto const sourceA = std::make_shared<CharStream>("lorem ipsum", "sourceA");
auto const sourceB = std::make_shared<CharStream>("lorem ipsum", "sourceB");
auto const source = std::make_shared<std::string>("source");
auto const sourceA = std::make_shared<std::string>("sourceA");
auto const sourceB = std::make_shared<std::string>("sourceB");
BOOST_CHECK(SourceLocation{} == SourceLocation{});
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))
{
SourceReferenceFormatter formatter(_stream, _formatted, false);
for (auto const& error: c.errors())
formatter.printErrorInformation(*error);
SourceReferenceFormatter formatter(_stream, c, _formatted, false);
formatter.printErrorInformation(c.errors());
return TestResult::FatalError;
}
@ -167,9 +166,8 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi
if (m_expectation.empty())
return resultsMatch ? TestResult::Success : TestResult::Failure;
SourceReferenceFormatter formatter(_stream, _formatted, false);
for (auto const& error: c.errors())
formatter.printErrorInformation(*error);
SourceReferenceFormatter{_stream, c, _formatted, false}
.printErrorInformation(c.errors());
return TestResult::FatalError;
}

View File

@ -151,7 +151,7 @@ string AnalysisFramework::formatErrors() 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)

View File

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

View File

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

View File

@ -81,7 +81,7 @@ std::optional<Error> parseAndReturnFirstError(
{
string 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);
}
error = e;

View File

@ -65,10 +65,8 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
for (auto const& error: m_compiler.errors())
if (error->type() == langutil::Error::Type::CodeGenerationError)
BOOST_THROW_EXCEPTION(*error);
langutil::SourceReferenceFormatter formatter(std::cerr, true, false);
for (auto const& error: m_compiler.errors())
formatter.printErrorInformation(*error);
langutil::SourceReferenceFormatter{std::cerr, m_compiler, true, false}
.printErrorInformation(m_compiler.errors());
BOOST_ERROR("Compiling contract failed");
}
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
{
public:
explicit CheckInlineAsmLocation(string _sourceCode): m_sourceCode(_sourceCode) {}
bool visited = false;
bool visit(InlineAssembly const& _inlineAsm) override
{
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 }");
visited = true;
return false;
}
string m_sourceCode;
};
CheckInlineAsmLocation visitor;
CheckInlineAsmLocation visitor{sourceCode};
contract->accept(visitor);
BOOST_CHECK_MESSAGE(visitor.visited, "No inline asm block found?!");

View File

@ -17,6 +17,7 @@
// SPDX-License-Identifier: GPL-3.0
#include <test/libsolidity/SyntaxTest.h>
#include <test/Common.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
@ -119,11 +120,13 @@ void SyntaxTest::filterObtainedErrors()
string sourceName;
if (auto location = boost::get_error_info<errinfo_sourceLocation>(*currentError))
{
solAssert(location->source, "");
sourceName = location->source->name();
solAssert(location->sourceName, "");
sourceName = *location->sourceName;
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, "");
// 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)
{
AssemblyStack stack(

View File

@ -44,8 +44,6 @@ struct Dialect;
namespace solidity::yul::test
{
void printErrors(langutil::ErrorList const& _errors);
std::pair<std::shared_ptr<Block>, std::shared_ptr<AsmAnalysisInfo>>
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))
{
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl;
SourceReferenceFormatter formatter(_stream, true, false);
for (auto const& error: stack.errors())
formatter.printErrorInformation(*error);
SourceReferenceFormatter{_stream, stack, true, false}
.printErrorInformation(stack.errors());
return TestResult::FatalError;
}

View File

@ -63,7 +63,8 @@ TestCase::TestResult EwasmTranslationTest::run(ostream& _stream, string const& _
return TestResult::FatalError;
*m_object = EVMToEwasmTranslator(
EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion())
EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()),
m_stack
).run(*m_object);
// 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)
{
AssemblyStack stack(
m_stack = AssemblyStack(
solidity::test::CommonOptions::get().evmVersion(),
AssemblyStack::Language::StrictAssembly,
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;
}
else
{
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;
}
}
@ -114,11 +116,3 @@ string EwasmTranslationTest::interpret()
state.dumpTraceAndState(result);
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>
namespace solidity::langutil
{
class Scanner;
class Error;
using ErrorList = std::vector<std::shared_ptr<Error const>>;
}
#include <libyul/AssemblyStack.h>
namespace solidity::yul
{
struct Object;
class AssemblyStack;
}
namespace solidity::yul::test
@ -51,9 +47,8 @@ private:
bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted);
std::string interpret();
static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors);
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))
{
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;
}
stack.optimize();
@ -104,11 +105,3 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li
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);
void disambiguate();
static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors);
frontend::OptimisationPreset m_optimisationPreset;
bool m_wasm = false;
};

View File

@ -51,24 +51,19 @@ namespace solidity::yul::test
namespace
{
string_view constexpr g_strAlternateSourceText = "{}";
shared_ptr<Block> parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorReporter)
{
try
{
auto scanner = make_shared<Scanner>(CharStream(_source, ""));
map<unsigned, shared_ptr<CharStream>> indicesToCharStreams;
indicesToCharStreams[0] = scanner->charStream();
indicesToCharStreams[1] = make_shared<CharStream>(
string(g_strAlternateSourceText.data(), g_strAlternateSourceText.size()),
"alternate.sol"
);
map<unsigned, shared_ptr<string const>> indicesToSourceNames;
indicesToSourceNames[0] = make_shared<string const>("source0");
indicesToSourceNames[1] = make_shared<string const>("source1");
auto parserResult = yul::Parser(
errorReporter,
_dialect,
move(indicesToCharStreams)
move(indicesToSourceNames)
).parse(scanner, false);
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 { \
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((_end), (_actual).end); \
} while (0)
@ -217,7 +212,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_empty_block)
EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
shared_ptr<Block> result = parse(sourceText, dialect, reporter);
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)
@ -235,12 +230,12 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_with_children)
EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
shared_ptr<Block> result = parse(sourceText, dialect, reporter);
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());
CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 234, 543);
CHECK_LOCATION(locationOf(result->statements.at(1)), sourceText, 123, 432);
CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 234, 543);
CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 123, 432);
// [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)
@ -258,12 +253,12 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_different_sources)
EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
shared_ptr<Block> result = parse(sourceText, dialect, reporter);
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());
CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 234, 543);
CHECK_LOCATION(locationOf(result->statements.at(1)), g_strAlternateSourceText, 123, 432);
CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 234, 543);
CHECK_LOCATION(locationOf(result->statements.at(1)), "source1", 123, 432);
// [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)
@ -280,9 +275,9 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_nested)
EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
shared_ptr<Block> result = parse(sourceText, dialect, reporter);
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());
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)
@ -304,19 +299,19 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_switch_case)
EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
shared_ptr<Block> result = parse(sourceText, dialect, reporter);
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(holds_alternative<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());
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;
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)
@ -337,19 +332,19 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope)
shared_ptr<Block> result = parse(sourceText, dialect, reporter);
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());
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.
BOOST_REQUIRE(holds_alternative<Block>(result->statements.at(0)));
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.
CHECK_LOCATION(locationOf(result->statements.at(1)), sourceText, 123, 432);
CHECK_LOCATION(locationOf(result->statements.at(2)), sourceText, 123, 432);
CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 123, 432);
CHECK_LOCATION(locationOf(result->statements.at(2)), "source0", 123, 432);
}
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);
BOOST_REQUIRE(!!result); // should still parse
BOOST_REQUIRE_EQUAL(2, result->statements.size());
CHECK_LOCATION(locationOf(result->statements.at(0)), sourceText, 123, 432);
CHECK_LOCATION(locationOf(result->statements.at(1)), g_strAlternateSourceText, 1, 10);
CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 123, 432);
CHECK_LOCATION(locationOf(result->statements.at(1)), "source1", 1, 10);
}
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_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)));
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)
@ -429,20 +424,20 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_2)
shared_ptr<Block> result = parse(sourceText, dialect, reporter);
BOOST_REQUIRE(!!result);
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, `
BOOST_REQUIRE(holds_alternative<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(holds_alternative<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`
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)
@ -463,24 +458,24 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_3)
shared_ptr<Block> result = parse(sourceText, dialect, reporter);
BOOST_REQUIRE(!!result);
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)));
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(holds_alternative<ExpressionStatement>(result->statements.at(1)));
ExpressionStatement const& sstoreStmt = get<ExpressionStatement>(innerBlock.statements.at(0));
BOOST_REQUIRE(holds_alternative<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)));
ExpressionStatement mstoreStmt = get<ExpressionStatement>(result->statements.at(1));
BOOST_REQUIRE(holds_alternative<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)
@ -499,11 +494,11 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_comments_after_valid)
shared_ptr<Block> result = parse(sourceText, dialect, reporter);
BOOST_REQUIRE(!!result);
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)));
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)
@ -553,7 +548,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_ensure_last_match)
VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0));
// 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)
@ -573,6 +568,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc)
BOOST_REQUIRE(holds_alternative<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);
}

View File

@ -78,7 +78,8 @@ bool YulInterpreterTest::parse(ostream& _stream, string const& _linePrefix, bool
else
{
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;
}
}
@ -101,11 +102,3 @@ string YulInterpreterTest::interpret()
state.dumpTraceAndState(result);
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>
namespace solidity::langutil
{
class Scanner;
class Error;
using ErrorList = std::vector<std::shared_ptr<Error const>>;
}
namespace solidity::yul
{
struct AsmAnalysisInfo;
@ -52,8 +45,6 @@ private:
bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted);
std::string interpret();
static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors);
std::shared_ptr<Block> m_ast;
std::shared_ptr<AsmAnalysisInfo> m_analysisInfo;
};

View File

@ -28,6 +28,7 @@
#include <libyul/AsmPrinter.h>
#include <liblangutil/SourceReferenceFormatter.h>
#include <liblangutil/Scanner.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))
{
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 {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::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;

View File

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

View File

@ -43,20 +43,6 @@ using namespace solidity::yul;
using namespace solidity::yul::test;
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)
{
ProtoConverter converter;
@ -88,7 +74,13 @@ DEFINE_PROTO_FUZZER(Program const& _input)
!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");
}

View File

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

View File

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

View File

@ -22,6 +22,9 @@
#include <libsolidity/ast/AST.h>
#include <liblangutil/CharStreamProvider.h>
#include <liblangutil/Scanner.h>
#include <sstream>
#include <regex>
@ -47,42 +50,57 @@ public:
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
* on a textual base. They utilize regular expression to search for
* keywords or to determine formatting.
*/
class SourceAnalysis
class SourceAnalysis: public SourceTextRetriever
{
public:
static bool isMultilineKeyword(
using SourceTextRetriever::SourceTextRetriever;
bool isMultilineKeyword(
langutil::SourceLocation const& _location,
std::string const& _keyword
)
) const
{
return regex_search(
_location.text(),
text(_location),
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(
_location.text(),
text(_location),
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
* to the given source location.
*/
class SourceTransform
class SourceTransform: public SourceTextRetriever
{
public:
using SourceTextRetriever::SourceTextRetriever;
/// Searches for the keyword given and prepends the expression.
/// E.g. `function f() view;` -> `function f() public view;`
static std::string insertBeforeKeyword(
std::string insertBeforeKeyword(
langutil::SourceLocation const& _location,
std::string const& _keyword,
std::string const& _expression
)
) const
{
auto _regex = std::regex{"(\\b" + _keyword + "\\b)"};
if (regex_search(_location.text(), _regex))
if (regex_search(text(_location), _regex))
return regex_replace(
_location.text(),
text(_location),
_regex,
_expression + " " + _keyword,
std::regex_constants::format_first_only
@ -139,7 +159,7 @@ public:
else
solAssert(
false,
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location <<
LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually."
);
@ -148,22 +168,22 @@ public:
/// Searches for the keyword given and appends the expression.
/// E.g. `function f() public {}` -> `function f() public override {}`
static std::string insertAfterKeyword(
std::string insertAfterKeyword(
langutil::SourceLocation const& _location,
std::string const& _keyword,
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::regex keyword{"(\\b" + _keyword + "\\b)"};
if (regex_search(_location.text(), keyword))
return regex_replace(_location.text(), keyword, _keyword + toAppend);
if (regex_search(text(_location), keyword))
return regex_replace(text(_location), keyword, _keyword + toAppend);
else
solAssert(
false,
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location <<
LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually."
);
@ -174,22 +194,22 @@ public:
/// Searches for the first right parenthesis and appends the expression
/// given.
/// E.g. `function f() {}` -> `function f() public {}`
static std::string insertAfterRightParenthesis(
std::string insertAfterRightParenthesis(
langutil::SourceLocation const& _location,
std::string const& _expression
)
) const
{
auto _regex = std::regex{"(\\))"};
if (regex_search(_location.text(), _regex))
if (regex_search(text(_location), _regex))
return regex_replace(
_location.text(),
text(_location),
std::regex{"(\\))"},
") " + _expression
);
else
solAssert(
false,
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location <<
LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually."
);
@ -199,38 +219,38 @@ public:
/// Searches for the `function` keyword and its identifier and replaces
/// both by the expression given.
/// E.g. `function Storage() {}` -> `constructor() {}`
static std::string replaceFunctionName(
std::string replaceFunctionName(
langutil::SourceLocation const& _location,
std::string const& _name,
std::string const& _expression
)
) const
{
auto _regex = std::regex{ "(\\bfunction\\s*" + _name + "\\b)"};
if (regex_search(_location.text(), _regex))
if (regex_search(text(_location), _regex))
return regex_replace(
_location.text(),
text(_location),
_regex,
_expression
);
else
solAssert(
false,
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location <<
LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually."
);
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
std::regex gasReg{"\\.gas\\s*\\("};
if (regex_search(_location.text(), gasReg))
if (regex_search(text(_location), gasReg))
{
std::string out = regex_replace(
_location.text(),
text(_location),
gasReg,
"{gas: ",
std::regex_constants::format_first_only
@ -240,22 +260,22 @@ public:
else
solAssert(
false,
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location <<
LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually."
);
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
std::regex valueReg{"\\.value\\s*\\("};
if (regex_search(_location.text(), valueReg))
if (regex_search(text(_location), valueReg))
{
std::string out = regex_replace(
_location.text(),
text(_location),
valueReg,
"{value: ",
std::regex_constants::format_first_only
@ -265,21 +285,21 @@ public:
else
solAssert(
false,
LocationHelper() << "Could not fix: " << _location.text() << " at " << _location <<
LocationHelper() << "Could not fix: " << text(_location) << " at " << _location <<
"\nNeeds to be fixed manually."
);
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"})
replacement = regex_replace(replacement, std::regex{replace}, "");
return replacement;

View File

@ -347,14 +347,14 @@ bool SourceUpgrade::analyzeAndUpgrade(pair<string, string> const& _sourceCode)
log() << "Analyzing and upgrading " << _sourceCode.first << "." << endl;
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())
{
auto& change = m_suite.changes().front();
if (verbose)
change.log(true);
change.log(*m_compiler, true);
if (change.level() == UpgradeChange::Level::Safe)
{
@ -388,20 +388,19 @@ void SourceUpgrade::applyChange(
log() << _change.patch();
}
_change.apply();
m_sourceCodes[_sourceCode.first] = _change.source();
m_sourceCodes[_sourceCode.first] = _change.apply(_sourceCode.second);
if (!dryRun)
writeInputFile(_sourceCode.first, _change.source());
writeInputFile(_sourceCode.first, m_sourceCodes[_sourceCode.first]);
}
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())
if (error->type() != langutil::Error::Type::Warning)
formatter->printErrorInformation(*error);
formatter.printErrorInformation(*error);
}
void SourceUpgrade::printStatistics() const

View File

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

View File

@ -36,7 +36,7 @@ void ConstructorKeyword::endVisit(ContractDefinition const& _contract)
m_changes.emplace_back(
UpgradeChange::Level::Safe,
function->location(),
SourceTransform::replaceFunctionName(
SourceTransform{m_charStreamProvider}.replaceFunctionName(
function->location(),
function->name(),
"constructor"
@ -50,6 +50,6 @@ void VisibilitySpecifier::endVisit(FunctionDefinition const& _function)
m_changes.emplace_back(
UpgradeChange::Level::Safe,
_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::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)
{
bool isFullyImplemented = _contract.annotation().unimplementedDeclarations->empty();
@ -110,7 +41,7 @@ void AbstractContract::endVisit(ContractDefinition const& _contract)
m_changes.emplace_back(
UpgradeChange::Level::Safe,
_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)
{
auto const& inheritedFunctions = m_overrideChecker.inheritedFunctions(_contract);
@ -192,12 +159,39 @@ void VirtualFunction::endVisit(ContractDefinition const& _contract)
)
{
m_changes.emplace_back(
UpgradeChange::Level::Safe,
function->location(),
appendVirtual(*function)
UpgradeChange::Level::Safe,
function->location(),
appendVirtual(*function)
);
}
}
}
}
}
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); }
private:
using Contracts = std::set<frontend::ContractDefinition const*, frontend::OverrideChecker::CompareByID>;
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); }
private:
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(
UpgradeChange::Level::Safe,
_functionCall.location(),
SourceTransform::valueUpdate(_functionCall.location())
SourceTransform{m_charStreamProvider}.valueUpdate(_functionCall.location())
);
if (funcType->gasSet())
m_changes.emplace_back(
UpgradeChange::Level::Safe,
_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(
UpgradeChange::Level::Safe,
_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(
UpgradeChange::Level::Safe,
_contract.location(),
SourceTransform::insertBeforeKeyword(_contract.location(), "contract", "abstract")
SourceTransform{m_charStreamProvider}.insertBeforeKeyword(_contract.location(), "contract", "abstract")
);
for (FunctionDefinition const* function: _contract.definedFunctions())
@ -80,6 +80,6 @@ void ConstructorVisibility::endVisit(ContractDefinition const& _contract)
m_changes.emplace_back(
UpgradeChange::Level::Safe,
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::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.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;
SourceReferenceFormatter formatter{os, true, false};
SourceReferenceFormatter formatter{os, _charStreamProvider, true, false};
string start = to_string(m_location.start);
string end = to_string(m_location.end);
@ -48,10 +50,10 @@ void UpgradeChange::log(bool const _shorten) const
os << endl;
AnsiColorized(os, true, {formatting::BOLD, color}) << "Upgrade change (" << level << ")" << endl;
os << "=======================" << endl;
formatter.printSourceLocation(SourceReferenceExtractor::extract(&m_location));
formatter.printSourceLocation(SourceReferenceExtractor::extract(_charStreamProvider, &m_location));
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;
stringstream output;

View File

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

View File

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

View File

@ -27,6 +27,8 @@
#include <tools/yulPhaser/SimulationRNG.h>
#include <liblangutil/CharStream.h>
#include <liblangutil/SourceReferenceFormatter.h>
#include <liblangutil/Scanner.h>
#include <libsolutil/Assertions.h>
#include <libsolutil/CommonData.h>
@ -389,7 +391,9 @@ vector<Program> ProgramFactory::build(Options const& _options)
variant<Program, ErrorList> programOrErrors = Program::load(sourceCode);
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);
}

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):
m_ast(make_unique<Block>(get<Block>(ASTCopier{}(*program.m_ast)))),
m_dialect{program.m_dialect},

View File

@ -35,6 +35,7 @@ namespace solidity::langutil
{
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
{