mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
@@ -155,12 +155,18 @@ set(sources
|
||||
interface/StorageLayout.h
|
||||
interface/Version.cpp
|
||||
interface/Version.h
|
||||
lsp/LanguageServer.cpp
|
||||
lsp/LanguageServer.h
|
||||
lsp/FileRepository.cpp
|
||||
lsp/FileRepository.h
|
||||
lsp/GotoDefinition.cpp
|
||||
lsp/GotoDefinition.h
|
||||
lsp/HandlerBase.cpp
|
||||
lsp/HandlerBase.h
|
||||
lsp/LanguageServer.cpp
|
||||
lsp/LanguageServer.h
|
||||
lsp/Transport.cpp
|
||||
lsp/Transport.h
|
||||
lsp/Utils.cpp
|
||||
lsp/Utils.h
|
||||
parsing/DocStringParser.cpp
|
||||
parsing/DocStringParser.h
|
||||
parsing/Parser.cpp
|
||||
|
||||
@@ -129,7 +129,7 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod
|
||||
// Propagate changes to all exits and queue them for traversal, if needed.
|
||||
for (auto const& exit: currentNode->exits)
|
||||
if (
|
||||
auto exists = valueOrNullptr(nodeInfos, exit);
|
||||
auto exists = util::valueOrNullptr(nodeInfos, exit);
|
||||
nodeInfos[exit].propagateFrom(nodeInfo) || !exists
|
||||
)
|
||||
nodesToTraverse.insert(exit);
|
||||
|
||||
@@ -457,8 +457,7 @@ void ControlFlowBuilder::operator()(yul::Switch const& _switch)
|
||||
}
|
||||
mergeFlow(nodes);
|
||||
|
||||
bool hasDefault = util::contains_if(_switch.cases, [](yul::Case const& _case) { return !_case.value; });
|
||||
if (!hasDefault)
|
||||
if (!hasDefaultCase(_switch))
|
||||
connect(beforeSwitch, m_currentNode);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ void ControlFlowRevertPruner::run()
|
||||
|
||||
void ControlFlowRevertPruner::findRevertStates()
|
||||
{
|
||||
std::set<CFG::FunctionContractTuple> pendingFunctions = keys(m_functions);
|
||||
std::set<CFG::FunctionContractTuple> pendingFunctions = util::keys(m_functions);
|
||||
// We interrupt the search whenever we encounter a call to a function with (yet) unknown
|
||||
// revert behaviour. The ``wakeUp`` data structure contains information about which
|
||||
// searches to restart once we know about the behaviour.
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <libsolutil/Algorithms.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <range/v3/view/transform.hpp>
|
||||
|
||||
@@ -451,12 +452,39 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
||||
|
||||
bool DeclarationTypeChecker::visit(UsingForDirective const& _usingFor)
|
||||
{
|
||||
ContractDefinition const* library = dynamic_cast<ContractDefinition const*>(
|
||||
_usingFor.libraryName().annotation().referencedDeclaration
|
||||
);
|
||||
if (_usingFor.usesBraces())
|
||||
{
|
||||
for (ASTPointer<IdentifierPath> const& function: _usingFor.functionsOrLibrary())
|
||||
if (auto functionDefinition = dynamic_cast<FunctionDefinition const*>(function->annotation().referencedDeclaration))
|
||||
{
|
||||
if (!functionDefinition->isFree() && !(
|
||||
dynamic_cast<ContractDefinition const*>(functionDefinition->scope()) &&
|
||||
dynamic_cast<ContractDefinition const*>(functionDefinition->scope())->isLibrary()
|
||||
))
|
||||
m_errorReporter.typeError(
|
||||
4167_error,
|
||||
function->location(),
|
||||
"Only file-level functions and library functions can be bound to a type in a \"using\" statement"
|
||||
);
|
||||
}
|
||||
else
|
||||
m_errorReporter.fatalTypeError(8187_error, function->location(), "Expected function name." );
|
||||
}
|
||||
else
|
||||
{
|
||||
ContractDefinition const* library = dynamic_cast<ContractDefinition const*>(
|
||||
_usingFor.functionsOrLibrary().front()->annotation().referencedDeclaration
|
||||
);
|
||||
if (!library || !library->isLibrary())
|
||||
m_errorReporter.fatalTypeError(
|
||||
4357_error,
|
||||
_usingFor.functionsOrLibrary().front()->location(),
|
||||
"Library name expected. If you want to attach a function, use '{...}'."
|
||||
);
|
||||
}
|
||||
|
||||
if (!library || !library->isLibrary())
|
||||
m_errorReporter.fatalTypeError(4357_error, _usingFor.libraryName().location(), "Library name expected.");
|
||||
// We do not visit _usingFor.functions() because it will lead to an error since
|
||||
// library names cannot be mentioned stand-alone.
|
||||
|
||||
if (_usingFor.typeName())
|
||||
_usingFor.typeName()->accept(*this);
|
||||
|
||||
@@ -97,7 +97,7 @@ CallGraph FunctionCallGraphBuilder::buildDeployedGraph(
|
||||
// assigned to state variables and as such may be reachable after deployment as well.
|
||||
builder.m_currentNode = CallGraph::SpecialNode::InternalDispatch;
|
||||
set<CallGraph::Node, CallGraph::CompareByID> defaultNode;
|
||||
for (CallGraph::Node const& dispatchTarget: valueOrDefault(_creationGraph.edges, CallGraph::SpecialNode::InternalDispatch, defaultNode))
|
||||
for (CallGraph::Node const& dispatchTarget: util::valueOrDefault(_creationGraph.edges, CallGraph::SpecialNode::InternalDispatch, defaultNode))
|
||||
{
|
||||
solAssert(!holds_alternative<CallGraph::SpecialNode>(dispatchTarget), "");
|
||||
solAssert(get<CallableDeclaration const*>(dispatchTarget) != nullptr, "");
|
||||
|
||||
@@ -411,12 +411,12 @@ struct ReservedErrorSelector: public PostTypeChecker::Checker
|
||||
);
|
||||
else
|
||||
{
|
||||
uint32_t selector = selectorFromSignature32(_error.functionType(true)->externalSignature());
|
||||
uint32_t selector = util::selectorFromSignature32(_error.functionType(true)->externalSignature());
|
||||
if (selector == 0 || ~selector == 0)
|
||||
m_errorReporter.syntaxError(
|
||||
2855_error,
|
||||
_error.location(),
|
||||
"The selector 0x" + toHex(toCompactBigEndian(selector, 4)) + " is reserved. Please rename the error to avoid the collision."
|
||||
"The selector 0x" + util::toHex(toCompactBigEndian(selector, 4)) + " is reserved. Please rename the error to avoid the collision."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ bool PostTypeContractLevelChecker::check(ContractDefinition const& _contract)
|
||||
for (ErrorDefinition const* error: _contract.interfaceErrors())
|
||||
{
|
||||
string signature = error->functionType(true)->externalSignature();
|
||||
uint32_t hash = selectorFromSignature32(signature);
|
||||
uint32_t hash = util::selectorFromSignature32(signature);
|
||||
// Fail if there is a different signature for the same hash.
|
||||
if (!errorHashes[hash].empty() && !errorHashes[hash].count(signature))
|
||||
{
|
||||
|
||||
@@ -403,6 +403,42 @@ void SyntaxChecker::endVisit(ContractDefinition const&)
|
||||
m_currentContractKind = std::nullopt;
|
||||
}
|
||||
|
||||
bool SyntaxChecker::visit(UsingForDirective const& _usingFor)
|
||||
{
|
||||
if (!m_currentContractKind && !_usingFor.typeName())
|
||||
m_errorReporter.syntaxError(
|
||||
8118_error,
|
||||
_usingFor.location(),
|
||||
"The type has to be specified explicitly at file level (cannot use '*')."
|
||||
);
|
||||
else if (_usingFor.usesBraces() && !_usingFor.typeName())
|
||||
m_errorReporter.syntaxError(
|
||||
3349_error,
|
||||
_usingFor.location(),
|
||||
"The type has to be specified explicitly when attaching specific functions."
|
||||
);
|
||||
if (_usingFor.global() && !_usingFor.typeName())
|
||||
m_errorReporter.syntaxError(
|
||||
2854_error,
|
||||
_usingFor.location(),
|
||||
"Can only globally bind functions to specific types."
|
||||
);
|
||||
if (_usingFor.global() && m_currentContractKind)
|
||||
m_errorReporter.syntaxError(
|
||||
3367_error,
|
||||
_usingFor.location(),
|
||||
"\"global\" can only be used at file level."
|
||||
);
|
||||
if (m_currentContractKind == ContractKind::Interface)
|
||||
m_errorReporter.syntaxError(
|
||||
9088_error,
|
||||
_usingFor.location(),
|
||||
"The \"using for\" directive is not allowed inside interfaces."
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SyntaxChecker::visit(FunctionDefinition const& _function)
|
||||
{
|
||||
solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), "");
|
||||
|
||||
@@ -88,6 +88,9 @@ private:
|
||||
|
||||
bool visit(ContractDefinition const& _contract) override;
|
||||
void endVisit(ContractDefinition const& _contract) override;
|
||||
|
||||
bool visit(UsingForDirective const& _usingFor) override;
|
||||
|
||||
bool visit(FunctionDefinition const& _function) override;
|
||||
bool visit(FunctionTypeName const& _node) override;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <libsolutil/Algorithms.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
#include <libsolutil/Views.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
@@ -267,6 +268,11 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio
|
||||
return {TypeProvider::meta(dynamic_cast<TypeType const&>(*firstArgType).actualType())};
|
||||
}
|
||||
|
||||
bool TypeChecker::visit(ImportDirective const&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
|
||||
{
|
||||
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
|
||||
@@ -659,7 +665,7 @@ void TypeChecker::visitManually(
|
||||
if (auto const* modifierContract = dynamic_cast<ContractDefinition const*>(modifierDecl->scope()))
|
||||
if (m_currentContract)
|
||||
{
|
||||
if (!contains(m_currentContract->annotation().linearizedBaseContracts, modifierContract))
|
||||
if (!util::contains(m_currentContract->annotation().linearizedBaseContracts, modifierContract))
|
||||
m_errorReporter.typeError(
|
||||
9428_error,
|
||||
_modifier.location(),
|
||||
@@ -2143,7 +2149,7 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
|
||||
functionPointerType->declaration().scope() == m_currentContract
|
||||
)
|
||||
msg += " Did you forget to prefix \"this.\"?";
|
||||
else if (contains(
|
||||
else if (util::contains(
|
||||
m_currentContract->annotation().linearizedBaseContracts,
|
||||
functionPointerType->declaration().scope()
|
||||
) && functionPointerType->declaration().scope() != m_currentContract)
|
||||
@@ -2204,9 +2210,9 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
|
||||
"Cannot implicitly convert component at position " +
|
||||
to_string(i) +
|
||||
" from \"" +
|
||||
argType.canonicalName() +
|
||||
argType.toString() +
|
||||
"\" to \"" +
|
||||
functionPointerType->parameterTypes()[i]->canonicalName() +
|
||||
functionPointerType->parameterTypes()[i]->toString() +
|
||||
"\"" +
|
||||
(result.message().empty() ? "." : ": " + result.message())
|
||||
);
|
||||
@@ -3635,12 +3641,89 @@ void TypeChecker::endVisit(Literal const& _literal)
|
||||
|
||||
void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
||||
{
|
||||
if (m_currentContract->isInterface())
|
||||
m_errorReporter.typeError(
|
||||
9088_error,
|
||||
_usingFor.location(),
|
||||
"The \"using for\" directive is not allowed inside interfaces."
|
||||
if (!_usingFor.usesBraces())
|
||||
{
|
||||
solAssert(_usingFor.functionsOrLibrary().size() == 1);
|
||||
ContractDefinition const* library = dynamic_cast<ContractDefinition const*>(
|
||||
_usingFor.functionsOrLibrary().front()->annotation().referencedDeclaration
|
||||
);
|
||||
solAssert(library && library->isLibrary());
|
||||
// No type checking for libraries
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_usingFor.typeName())
|
||||
{
|
||||
solAssert(m_errorReporter.hasErrors());
|
||||
return;
|
||||
}
|
||||
|
||||
solAssert(_usingFor.typeName()->annotation().type);
|
||||
Type const* normalizedType = TypeProvider::withLocationIfReference(
|
||||
DataLocation::Storage,
|
||||
_usingFor.typeName()->annotation().type
|
||||
);
|
||||
solAssert(normalizedType);
|
||||
|
||||
if (_usingFor.global())
|
||||
{
|
||||
if (m_currentContract)
|
||||
solAssert(m_errorReporter.hasErrors());
|
||||
if (Declaration const* typeDefinition = _usingFor.typeName()->annotation().type->typeDefinition())
|
||||
{
|
||||
if (typeDefinition->scope() != m_currentSourceUnit)
|
||||
m_errorReporter.typeError(
|
||||
4117_error,
|
||||
_usingFor.location(),
|
||||
"Can only use \"global\" with types defined in the same source unit at file level."
|
||||
);
|
||||
}
|
||||
else
|
||||
m_errorReporter.typeError(
|
||||
8841_error,
|
||||
_usingFor.location(),
|
||||
"Can only use \"global\" with user-defined types."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
for (ASTPointer<IdentifierPath> const& path: _usingFor.functionsOrLibrary())
|
||||
{
|
||||
solAssert(path->annotation().referencedDeclaration);
|
||||
FunctionDefinition const& functionDefinition =
|
||||
dynamic_cast<FunctionDefinition const&>(*path->annotation().referencedDeclaration);
|
||||
|
||||
solAssert(functionDefinition.type());
|
||||
|
||||
if (functionDefinition.parameters().empty())
|
||||
m_errorReporter.fatalTypeError(
|
||||
4731_error,
|
||||
path->location(),
|
||||
"The function \"" + joinHumanReadable(path->path(), ".") + "\" " +
|
||||
"does not have any parameters, and therefore cannot be bound to the type \"" +
|
||||
(normalizedType ? normalizedType->toString(true) : "*") + "\"."
|
||||
);
|
||||
|
||||
FunctionType const* functionType = dynamic_cast<FunctionType const&>(*functionDefinition.type()).asBoundFunction();
|
||||
solAssert(functionType && functionType->selfType(), "");
|
||||
BoolResult result = normalizedType->isImplicitlyConvertibleTo(
|
||||
*TypeProvider::withLocationIfReference(DataLocation::Storage, functionType->selfType())
|
||||
);
|
||||
if (!result)
|
||||
m_errorReporter.typeError(
|
||||
3100_error,
|
||||
path->location(),
|
||||
"The function \"" + joinHumanReadable(path->path(), ".") + "\" "+
|
||||
"cannot be bound to the type \"" + _usingFor.typeName()->annotation().type->toString() +
|
||||
"\" because the type cannot be implicitly converted to the first argument" +
|
||||
" of the function (\"" + functionType->selfType()->toString() + "\")" +
|
||||
(
|
||||
result.message().empty() ?
|
||||
"." :
|
||||
": " + result.message()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void TypeChecker::checkErrorAndEventParameters(CallableDeclaration const& _callable)
|
||||
|
||||
@@ -125,6 +125,8 @@ private:
|
||||
FunctionType const* _functionType
|
||||
);
|
||||
|
||||
bool visit(ImportDirective const&) override;
|
||||
|
||||
void endVisit(InheritanceSpecifier const& _inheritance) override;
|
||||
void endVisit(ModifierDefinition const& _modifier) override;
|
||||
bool visit(FunctionDefinition const& _function) override;
|
||||
|
||||
@@ -134,6 +134,11 @@ bool ViewPureChecker::check()
|
||||
return !m_errors;
|
||||
}
|
||||
|
||||
bool ViewPureChecker::visit(ImportDirective const&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ViewPureChecker::visit(FunctionDefinition const& _funDef)
|
||||
{
|
||||
solAssert(!m_currentFunction, "");
|
||||
|
||||
@@ -50,6 +50,8 @@ private:
|
||||
langutil::SourceLocation location;
|
||||
};
|
||||
|
||||
bool visit(ImportDirective const&) override;
|
||||
|
||||
bool visit(FunctionDefinition const& _funDef) override;
|
||||
void endVisit(FunctionDefinition const& _funDef) override;
|
||||
bool visit(ModifierDefinition const& _modifierDef) override;
|
||||
|
||||
@@ -221,7 +221,7 @@ vector<EventDefinition const*> const ContractDefinition::usedInterfaceEvents() c
|
||||
{
|
||||
solAssert(annotation().creationCallGraph.set(), "");
|
||||
|
||||
return convertContainer<std::vector<EventDefinition const*>>(
|
||||
return util::convertContainer<std::vector<EventDefinition const*>>(
|
||||
(*annotation().creationCallGraph)->emittedEvents +
|
||||
(*annotation().deployedCallGraph)->emittedEvents
|
||||
);
|
||||
@@ -239,7 +239,7 @@ vector<ErrorDefinition const*> ContractDefinition::interfaceErrors(bool _require
|
||||
result +=
|
||||
(*annotation().creationCallGraph)->usedErrors +
|
||||
(*annotation().deployedCallGraph)->usedErrors;
|
||||
return convertContainer<vector<ErrorDefinition const*>>(move(result));
|
||||
return util::convertContainer<vector<ErrorDefinition const*>>(move(result));
|
||||
}
|
||||
|
||||
vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList(bool _includeInheritedFunctions) const
|
||||
|
||||
+33
-9
@@ -33,6 +33,7 @@
|
||||
#include <libevmasm/Instruction.h>
|
||||
#include <libsolutil/FixedHash.h>
|
||||
#include <libsolutil/LazyInit.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
@@ -630,9 +631,20 @@ private:
|
||||
};
|
||||
|
||||
/**
|
||||
* `using LibraryName for uint` will attach all functions from the library LibraryName
|
||||
* to `uint` if the first parameter matches the type. `using LibraryName for *` attaches
|
||||
* the function to any matching type.
|
||||
* Using for directive:
|
||||
*
|
||||
* 1. `using LibraryName for T` attaches all functions from the library `LibraryName` to the type `T`
|
||||
* 2. `using LibraryName for *` attaches to all types.
|
||||
* 3. `using {f1, f2, ..., fn} for T` attaches the functions `f1`, `f2`, ...,
|
||||
* `fn`, respectively to `T`.
|
||||
*
|
||||
* For version 3, T has to be implicitly convertible to the first parameter type of
|
||||
* all functions, and this is checked at the point of the using statement. For versions 1 and
|
||||
* 2, this check is only done when a function is called.
|
||||
*
|
||||
* Finally, `using {f1, f2, ..., fn} for T global` is also valid at file level, as long as T is
|
||||
* a user-defined type defined in the same file at file level. In this case, the methods are
|
||||
* attached to all objects of that type regardless of scope.
|
||||
*/
|
||||
class UsingForDirective: public ASTNode
|
||||
{
|
||||
@@ -640,24 +652,36 @@ public:
|
||||
UsingForDirective(
|
||||
int64_t _id,
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<IdentifierPath> _libraryName,
|
||||
ASTPointer<TypeName> _typeName
|
||||
std::vector<ASTPointer<IdentifierPath>> _functions,
|
||||
bool _usesBraces,
|
||||
ASTPointer<TypeName> _typeName,
|
||||
bool _global
|
||||
):
|
||||
ASTNode(_id, _location), m_libraryName(std::move(_libraryName)), m_typeName(std::move(_typeName))
|
||||
ASTNode(_id, _location),
|
||||
m_functions(_functions),
|
||||
m_usesBraces(_usesBraces),
|
||||
m_typeName(std::move(_typeName)),
|
||||
m_global{_global}
|
||||
{
|
||||
solAssert(m_libraryName != nullptr, "Name cannot be null.");
|
||||
}
|
||||
|
||||
void accept(ASTVisitor& _visitor) override;
|
||||
void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
IdentifierPath const& libraryName() const { return *m_libraryName; }
|
||||
/// @returns the type name the library is attached to, null for `*`.
|
||||
TypeName const* typeName() const { return m_typeName.get(); }
|
||||
|
||||
/// @returns a list of functions or the single library.
|
||||
std::vector<ASTPointer<IdentifierPath>> const& functionsOrLibrary() const { return m_functions; }
|
||||
bool usesBraces() const { return m_usesBraces; }
|
||||
bool global() const { return m_global; }
|
||||
|
||||
private:
|
||||
ASTPointer<IdentifierPath> m_libraryName;
|
||||
/// Either the single library or a list of functions.
|
||||
std::vector<ASTPointer<IdentifierPath>> m_functions;
|
||||
bool m_usesBraces;
|
||||
ASTPointer<TypeName> m_typeName;
|
||||
bool m_global = false;
|
||||
};
|
||||
|
||||
class StructDefinition: public Declaration, public ScopeOpener
|
||||
|
||||
@@ -47,7 +47,6 @@ namespace solidity::frontend
|
||||
|
||||
class Type;
|
||||
class ArrayType;
|
||||
using namespace util;
|
||||
|
||||
struct CallGraph;
|
||||
|
||||
@@ -91,13 +90,13 @@ struct StructurallyDocumentedAnnotation
|
||||
struct SourceUnitAnnotation: ASTAnnotation
|
||||
{
|
||||
/// The "absolute" (in the compiler sense) path of this source unit.
|
||||
SetOnce<std::string> path;
|
||||
util::SetOnce<std::string> path;
|
||||
/// The exported symbols (all global symbols).
|
||||
SetOnce<std::map<ASTString, std::vector<Declaration const*>>> exportedSymbols;
|
||||
util::SetOnce<std::map<ASTString, std::vector<Declaration const*>>> exportedSymbols;
|
||||
/// Experimental features.
|
||||
std::set<ExperimentalFeature> experimentalFeatures;
|
||||
/// Using the new ABI coder. Set to `false` if using ABI coder v1.
|
||||
SetOnce<bool> useABICoderV2;
|
||||
util::SetOnce<bool> useABICoderV2;
|
||||
};
|
||||
|
||||
struct ScopableAnnotation
|
||||
@@ -127,7 +126,7 @@ struct DeclarationAnnotation: ASTAnnotation, ScopableAnnotation
|
||||
struct ImportAnnotation: DeclarationAnnotation
|
||||
{
|
||||
/// The absolute path of the source unit to import.
|
||||
SetOnce<std::string> absolutePath;
|
||||
util::SetOnce<std::string> absolutePath;
|
||||
/// The actual source unit.
|
||||
SourceUnit const* sourceUnit = nullptr;
|
||||
};
|
||||
@@ -135,7 +134,7 @@ struct ImportAnnotation: DeclarationAnnotation
|
||||
struct TypeDeclarationAnnotation: DeclarationAnnotation
|
||||
{
|
||||
/// The name of this type, prefixed by proper namespaces if globally accessible.
|
||||
SetOnce<std::string> canonicalName;
|
||||
util::SetOnce<std::string> canonicalName;
|
||||
};
|
||||
|
||||
struct StructDeclarationAnnotation: TypeDeclarationAnnotation
|
||||
@@ -162,9 +161,9 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocu
|
||||
/// These can either be inheritance specifiers or modifier invocations.
|
||||
std::map<FunctionDefinition const*, ASTNode const*> baseConstructorArguments;
|
||||
/// A graph with edges representing calls between functions that may happen during contract construction.
|
||||
SetOnce<std::shared_ptr<CallGraph const>> creationCallGraph;
|
||||
util::SetOnce<std::shared_ptr<CallGraph const>> creationCallGraph;
|
||||
/// A graph with edges representing calls between functions that may happen in a deployed contract.
|
||||
SetOnce<std::shared_ptr<CallGraph const>> deployedCallGraph;
|
||||
util::SetOnce<std::shared_ptr<CallGraph const>> deployedCallGraph;
|
||||
|
||||
/// List of contracts whose bytecode is referenced by this contract, e.g. through "new".
|
||||
/// The Value represents the ast node that referenced the contract.
|
||||
@@ -223,7 +222,7 @@ struct InlineAssemblyAnnotation: StatementAnnotation
|
||||
/// True, if the assembly block was annotated to be memory-safe.
|
||||
bool markedMemorySafe = false;
|
||||
/// True, if the assembly block involves any memory opcode or assigns to variables in memory.
|
||||
SetOnce<bool> hasMemoryEffects;
|
||||
util::SetOnce<bool> hasMemoryEffects;
|
||||
};
|
||||
|
||||
struct BlockAnnotation: StatementAnnotation, ScopableAnnotation
|
||||
@@ -256,7 +255,7 @@ struct IdentifierPathAnnotation: ASTAnnotation
|
||||
/// Referenced declaration, set during reference resolution stage.
|
||||
Declaration const* referencedDeclaration = nullptr;
|
||||
/// What kind of lookup needs to be done (static, virtual, super) find the declaration.
|
||||
SetOnce<VirtualLookup> requiredLookup;
|
||||
util::SetOnce<VirtualLookup> requiredLookup;
|
||||
};
|
||||
|
||||
struct ExpressionAnnotation: ASTAnnotation
|
||||
@@ -264,11 +263,11 @@ struct ExpressionAnnotation: ASTAnnotation
|
||||
/// Inferred type of the expression.
|
||||
Type const* type = nullptr;
|
||||
/// Whether the expression is a constant variable
|
||||
SetOnce<bool> isConstant;
|
||||
util::SetOnce<bool> isConstant;
|
||||
/// Whether the expression is pure, i.e. compile-time constant.
|
||||
SetOnce<bool> isPure;
|
||||
util::SetOnce<bool> isPure;
|
||||
/// Whether it is an LValue (i.e. something that can be assigned to).
|
||||
SetOnce<bool> isLValue;
|
||||
util::SetOnce<bool> isLValue;
|
||||
/// Whether the expression is used in a context where the LValue is actually required.
|
||||
bool willBeWrittenTo = false;
|
||||
/// Whether the expression is an lvalue that is only assigned.
|
||||
@@ -295,7 +294,7 @@ struct IdentifierAnnotation: ExpressionAnnotation
|
||||
/// Referenced declaration, set at latest during overload resolution stage.
|
||||
Declaration const* referencedDeclaration = nullptr;
|
||||
/// What kind of lookup needs to be done (static, virtual, super) find the declaration.
|
||||
SetOnce<VirtualLookup> requiredLookup;
|
||||
util::SetOnce<VirtualLookup> requiredLookup;
|
||||
/// List of possible declarations it could refer to (can contain duplicates).
|
||||
std::vector<Declaration const*> candidateDeclarations;
|
||||
/// List of possible declarations it could refer to.
|
||||
@@ -307,7 +306,7 @@ struct MemberAccessAnnotation: ExpressionAnnotation
|
||||
/// Referenced declaration, set at latest during overload resolution stage.
|
||||
Declaration const* referencedDeclaration = nullptr;
|
||||
/// What kind of lookup needs to be done (static, virtual, super) find the declaration.
|
||||
SetOnce<VirtualLookup> requiredLookup;
|
||||
util::SetOnce<VirtualLookup> requiredLookup;
|
||||
};
|
||||
|
||||
struct BinaryOperationAnnotation: ExpressionAnnotation
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <libsolutil/JSON.h>
|
||||
#include <libsolutil/UTF8.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
#include <libsolutil/Keccak256.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
@@ -311,10 +312,26 @@ bool ASTJsonConverter::visit(InheritanceSpecifier const& _node)
|
||||
|
||||
bool ASTJsonConverter::visit(UsingForDirective const& _node)
|
||||
{
|
||||
setJsonNode(_node, "UsingForDirective", {
|
||||
make_pair("libraryName", toJson(_node.libraryName())),
|
||||
vector<pair<string, Json::Value>> attributes = {
|
||||
make_pair("typeName", _node.typeName() ? toJson(*_node.typeName()) : Json::nullValue)
|
||||
});
|
||||
};
|
||||
if (_node.usesBraces())
|
||||
{
|
||||
Json::Value functionList;
|
||||
for (auto const& function: _node.functionsOrLibrary())
|
||||
{
|
||||
Json::Value functionNode;
|
||||
functionNode["function"] = toJson(*function);
|
||||
functionList.append(move(functionNode));
|
||||
}
|
||||
attributes.emplace_back("functionList", move(functionList));
|
||||
}
|
||||
else
|
||||
attributes.emplace_back("libraryName", toJson(*_node.functionsOrLibrary().front()));
|
||||
attributes.emplace_back("global", _node.global());
|
||||
|
||||
setJsonNode(_node, "UsingForDirective", move(attributes));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -505,7 +522,7 @@ bool ASTJsonConverter::visit(EventDefinition const& _node)
|
||||
_attributes.emplace_back(
|
||||
make_pair(
|
||||
"eventSelector",
|
||||
toHex(u256(h256::Arith(util::keccak256(_node.functionType(true)->externalSignature()))))
|
||||
toHex(u256(util::h256::Arith(util::keccak256(_node.functionType(true)->externalSignature()))))
|
||||
));
|
||||
|
||||
setJsonNode(_node, "EventDefinition", std::move(_attributes));
|
||||
|
||||
@@ -348,10 +348,19 @@ ASTPointer<InheritanceSpecifier> ASTJsonImporter::createInheritanceSpecifier(Jso
|
||||
|
||||
ASTPointer<UsingForDirective> ASTJsonImporter::createUsingForDirective(Json::Value const& _node)
|
||||
{
|
||||
vector<ASTPointer<IdentifierPath>> functions;
|
||||
if (_node.isMember("libraryName"))
|
||||
functions.emplace_back(createIdentifierPath(_node["libraryName"]));
|
||||
else if (_node.isMember("functionList"))
|
||||
for (Json::Value const& function: _node["functionList"])
|
||||
functions.emplace_back(createIdentifierPath(function["function"]));
|
||||
|
||||
return createASTNode<UsingForDirective>(
|
||||
_node,
|
||||
createIdentifierPath(member(_node, "libraryName")),
|
||||
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"])
|
||||
move(functions),
|
||||
!_node.isMember("libraryName"),
|
||||
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"]),
|
||||
memberAsBool(_node, "global")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,35 @@
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/ASTUtils.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
|
||||
#include <libsolutil/Algorithms.h>
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
ASTNode const* locateInnermostASTNode(int _offsetInFile, SourceUnit const& _sourceUnit)
|
||||
{
|
||||
ASTNode const* innermostMatch = nullptr;
|
||||
auto locator = SimpleASTVisitor(
|
||||
[&](ASTNode const& _node) -> bool
|
||||
{
|
||||
// In the AST parent location always covers the whole child location.
|
||||
// The parent is visited first so to get the innermost node we simply
|
||||
// take the last one that still contains the offset.
|
||||
|
||||
if (!_node.location().containsOffset(_offsetInFile))
|
||||
return false;
|
||||
|
||||
innermostMatch = &_node;
|
||||
return true;
|
||||
},
|
||||
[](ASTNode const&) {}
|
||||
);
|
||||
_sourceUnit.accept(locator);
|
||||
return innermostMatch;
|
||||
}
|
||||
|
||||
bool isConstantVariableRecursive(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
solAssert(_varDecl.isConstant(), "Constant variable expected");
|
||||
|
||||
@@ -21,9 +21,11 @@
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
class VariableDeclaration;
|
||||
class ASTNode;
|
||||
class Declaration;
|
||||
class Expression;
|
||||
class SourceUnit;
|
||||
class VariableDeclaration;
|
||||
|
||||
/// Find the topmost referenced constant variable declaration when the given variable
|
||||
/// declaration value is an identifier. Works only for constant variable declarations.
|
||||
@@ -33,4 +35,7 @@ VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration cons
|
||||
/// Returns true if the constant variable declaration is recursive.
|
||||
bool isConstantVariableRecursive(VariableDeclaration const& _varDecl);
|
||||
|
||||
/// Returns the innermost AST node that covers the given location or nullptr if not found.
|
||||
ASTNode const* locateInnermostASTNode(int _offsetInFile, SourceUnit const& _sourceUnit);
|
||||
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ void UsingForDirective::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
{
|
||||
m_libraryName->accept(_visitor);
|
||||
listAccept(functionsOrLibrary(), _visitor);
|
||||
if (m_typeName)
|
||||
m_typeName->accept(_visitor);
|
||||
}
|
||||
@@ -205,7 +205,7 @@ void UsingForDirective::accept(ASTConstVisitor& _visitor) const
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
{
|
||||
m_libraryName->accept(_visitor);
|
||||
listAccept(functionsOrLibrary(), _visitor);
|
||||
if (m_typeName)
|
||||
m_typeName->accept(_visitor);
|
||||
}
|
||||
|
||||
+74
-24
@@ -33,7 +33,9 @@
|
||||
#include <libsolutil/CommonIO.h>
|
||||
#include <libsolutil/FunctionSelector.h>
|
||||
#include <libsolutil/Keccak256.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
#include <libsolutil/UTF8.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
@@ -330,30 +332,55 @@ Type const* Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool) c
|
||||
MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _scope)
|
||||
{
|
||||
vector<UsingForDirective const*> usingForDirectives;
|
||||
if (auto const* sourceUnit = dynamic_cast<SourceUnit const*>(&_scope))
|
||||
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes());
|
||||
else if (auto const* contract = dynamic_cast<ContractDefinition const*>(&_scope))
|
||||
usingForDirectives +=
|
||||
contract->usingForDirectives() +
|
||||
ASTNode::filteredNodes<UsingForDirective>(contract->sourceUnit().nodes());
|
||||
SourceUnit const* sourceUnit = dynamic_cast<SourceUnit const*>(&_scope);
|
||||
if (auto const* contract = dynamic_cast<ContractDefinition const*>(&_scope))
|
||||
{
|
||||
sourceUnit = &contract->sourceUnit();
|
||||
usingForDirectives += contract->usingForDirectives();
|
||||
}
|
||||
else
|
||||
solAssert(false, "");
|
||||
solAssert(sourceUnit, "");
|
||||
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes());
|
||||
|
||||
if (Declaration const* typeDefinition = _type.typeDefinition())
|
||||
if (auto const* sourceUnit = dynamic_cast<SourceUnit const*>(typeDefinition->scope()))
|
||||
for (auto usingFor: ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes()))
|
||||
// We do not yet compare the type name because of normalization.
|
||||
if (usingFor->global() && usingFor->typeName())
|
||||
usingForDirectives.emplace_back(usingFor);
|
||||
|
||||
// Normalise data location of type.
|
||||
DataLocation typeLocation = DataLocation::Storage;
|
||||
if (auto refType = dynamic_cast<ReferenceType const*>(&_type))
|
||||
typeLocation = refType->location();
|
||||
|
||||
set<Declaration const*> seenFunctions;
|
||||
MemberList::MemberMap members;
|
||||
|
||||
set<pair<string, Declaration const*>> seenFunctions;
|
||||
auto addFunction = [&](FunctionDefinition const& _function, optional<string> _name = {})
|
||||
{
|
||||
if (!_name)
|
||||
_name = _function.name();
|
||||
Type const* functionType =
|
||||
_function.libraryFunction() ? _function.typeViaContractName() : _function.type();
|
||||
solAssert(functionType, "");
|
||||
FunctionType const* asBoundFunction =
|
||||
dynamic_cast<FunctionType const&>(*functionType).asBoundFunction();
|
||||
solAssert(asBoundFunction, "");
|
||||
|
||||
if (_type.isImplicitlyConvertibleTo(*asBoundFunction->selfType()))
|
||||
if (seenFunctions.insert(make_pair(*_name, &_function)).second)
|
||||
members.emplace_back(&_function, asBoundFunction, *_name);
|
||||
};
|
||||
|
||||
for (UsingForDirective const* ufd: usingForDirectives)
|
||||
{
|
||||
// Convert both types to pointers for comparison to see if the `using for`
|
||||
// directive applies.
|
||||
// Further down, we check more detailed for each function if `_type` is
|
||||
// convertible to the function parameter type.
|
||||
if (ufd->typeName() &&
|
||||
if (
|
||||
ufd->typeName() &&
|
||||
*TypeProvider::withLocationIfReference(typeLocation, &_type, true) !=
|
||||
*TypeProvider::withLocationIfReference(
|
||||
typeLocation,
|
||||
@@ -362,20 +389,28 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc
|
||||
)
|
||||
)
|
||||
continue;
|
||||
auto const& library = dynamic_cast<ContractDefinition const&>(
|
||||
*ufd->libraryName().annotation().referencedDeclaration
|
||||
);
|
||||
for (FunctionDefinition const* function: library.definedFunctions())
|
||||
|
||||
for (auto const& pathPointer: ufd->functionsOrLibrary())
|
||||
{
|
||||
if (!function->isOrdinary() || !function->isVisibleAsLibraryMember() || seenFunctions.count(function))
|
||||
continue;
|
||||
seenFunctions.insert(function);
|
||||
if (function->parameters().empty())
|
||||
continue;
|
||||
FunctionTypePointer fun =
|
||||
dynamic_cast<FunctionType const&>(*function->typeViaContractName()).asBoundFunction();
|
||||
if (_type.isImplicitlyConvertibleTo(*fun->selfType()))
|
||||
members.emplace_back(function, fun);
|
||||
solAssert(pathPointer);
|
||||
Declaration const* declaration = pathPointer->annotation().referencedDeclaration;
|
||||
solAssert(declaration);
|
||||
|
||||
if (ContractDefinition const* library = dynamic_cast<ContractDefinition const*>(declaration))
|
||||
{
|
||||
solAssert(library->isLibrary());
|
||||
for (FunctionDefinition const* function: library->definedFunctions())
|
||||
{
|
||||
if (!function->isOrdinary() || !function->isVisibleAsLibraryMember() || function->parameters().empty())
|
||||
continue;
|
||||
addFunction(*function);
|
||||
}
|
||||
}
|
||||
else
|
||||
addFunction(
|
||||
dynamic_cast<FunctionDefinition const&>(*declaration),
|
||||
pathPointer->path().back()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -781,8 +816,8 @@ tuple<bool, rational> RationalNumberType::parseRational(string const& _value)
|
||||
if (radixPoint != _value.end())
|
||||
{
|
||||
if (
|
||||
!all_of(radixPoint + 1, _value.end(), ::isdigit) ||
|
||||
!all_of(_value.begin(), radixPoint, ::isdigit)
|
||||
!all_of(radixPoint + 1, _value.end(), util::isDigit) ||
|
||||
!all_of(_value.begin(), radixPoint, util::isDigit)
|
||||
)
|
||||
return make_tuple(false, rational(0));
|
||||
|
||||
@@ -2344,6 +2379,11 @@ TypeResult StructType::interfaceType(bool _inLibrary) const
|
||||
return *m_interfaceType_library;
|
||||
}
|
||||
|
||||
Declaration const* StructType::typeDefinition() const
|
||||
{
|
||||
return &structDefinition();
|
||||
}
|
||||
|
||||
BoolResult StructType::validForLocation(DataLocation _loc) const
|
||||
{
|
||||
for (auto const& member: m_struct.members())
|
||||
@@ -2476,6 +2516,11 @@ Type const* EnumType::encodingType() const
|
||||
return TypeProvider::uint(8);
|
||||
}
|
||||
|
||||
Declaration const* EnumType::typeDefinition() const
|
||||
{
|
||||
return &enumDefinition();
|
||||
}
|
||||
|
||||
TypeResult EnumType::unaryOperatorResult(Token _operator) const
|
||||
{
|
||||
return _operator == Token::Delete ? TypeProvider::emptyTuple() : nullptr;
|
||||
@@ -2544,6 +2589,11 @@ Type const& UserDefinedValueType::underlyingType() const
|
||||
return *type;
|
||||
}
|
||||
|
||||
Declaration const* UserDefinedValueType::typeDefinition() const
|
||||
{
|
||||
return &m_definition;
|
||||
}
|
||||
|
||||
string UserDefinedValueType::richIdentifier() const
|
||||
{
|
||||
return "t_userDefinedValueType" + parenthesizeIdentifier(m_definition.name()) + to_string(m_definition.id());
|
||||
|
||||
@@ -369,6 +369,10 @@ public:
|
||||
/// are returned without modification.
|
||||
virtual TypeResult interfaceType(bool /*_inLibrary*/) const { return nullptr; }
|
||||
|
||||
/// @returns the declaration of a user defined type (enum, struct, user defined value type).
|
||||
/// Returns nullptr otherwise.
|
||||
virtual Declaration const* typeDefinition() const { return nullptr; }
|
||||
|
||||
/// Clears all internally cached values (if any).
|
||||
virtual void clearCache() const;
|
||||
|
||||
@@ -1004,6 +1008,8 @@ public:
|
||||
Type const* encodingType() const override;
|
||||
TypeResult interfaceType(bool _inLibrary) const override;
|
||||
|
||||
Declaration const* typeDefinition() const override;
|
||||
|
||||
BoolResult validForLocation(DataLocation _loc) const override;
|
||||
|
||||
bool recursive() const;
|
||||
@@ -1069,6 +1075,8 @@ public:
|
||||
return _inLibrary ? this : encodingType();
|
||||
}
|
||||
|
||||
Declaration const* typeDefinition() const override;
|
||||
|
||||
EnumDefinition const& enumDefinition() const { return m_enum; }
|
||||
/// @returns the value that the string has in the Enum
|
||||
unsigned int memberValue(ASTString const& _member) const;
|
||||
@@ -1101,6 +1109,9 @@ public:
|
||||
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
|
||||
Type const* encodingType() const override { return &underlyingType(); }
|
||||
TypeResult interfaceType(bool /* _inLibrary */) const override {return &underlyingType(); }
|
||||
|
||||
Declaration const* typeDefinition() const override;
|
||||
|
||||
std::string richIdentifier() const override;
|
||||
bool operator==(Type const& _other) const override;
|
||||
|
||||
|
||||
@@ -403,7 +403,7 @@ void CompilerContext::appendInlineAssembly(
|
||||
{
|
||||
if (_insideFunction)
|
||||
return false;
|
||||
return contains(_localVariables, _identifier.name.str());
|
||||
return util::contains(_localVariables, _identifier.name.str());
|
||||
};
|
||||
identifierAccess.generateCode = [&](
|
||||
yul::Identifier const& _identifier,
|
||||
@@ -572,8 +572,7 @@ void CompilerContext::updateSourceLocation()
|
||||
evmasm::Assembly::OptimiserSettings CompilerContext::translateOptimiserSettings(OptimiserSettings const& _settings)
|
||||
{
|
||||
// Constructing it this way so that we notice changes in the fields.
|
||||
evmasm::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, false, m_evmVersion, 0};
|
||||
asmSettings.isCreation = true;
|
||||
evmasm::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, m_evmVersion, 0};
|
||||
asmSettings.runInliner = _settings.runInliner;
|
||||
asmSettings.runJumpdestRemover = _settings.runJumpdestRemover;
|
||||
asmSettings.runPeephole = _settings.runPeephole;
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
RevertStrings _revertStrings,
|
||||
CompilerContext* _runtimeContext = nullptr
|
||||
):
|
||||
m_asm(std::make_shared<evmasm::Assembly>()),
|
||||
m_asm(std::make_shared<evmasm::Assembly>(_runtimeContext != nullptr, std::string{})),
|
||||
m_evmVersion(_evmVersion),
|
||||
m_revertStrings(_revertStrings),
|
||||
m_reservedMemory{0},
|
||||
|
||||
@@ -235,7 +235,7 @@ size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract)
|
||||
m_context.pushSubroutineOffset(m_context.runtimeSub());
|
||||
// This code replaces the address added by appendDeployTimeAddress().
|
||||
m_context.appendInlineAssembly(
|
||||
Whiskers(R"(
|
||||
util::Whiskers(R"(
|
||||
{
|
||||
// If code starts at 11, an mstore(0) writes to the full PUSH20 plus data
|
||||
// without the need for a shift.
|
||||
@@ -672,7 +672,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
StackTooDeepError() <<
|
||||
errinfo_sourceLocation(_function.location()) <<
|
||||
errinfo_comment("Stack too deep, try removing local variables.")
|
||||
util::errinfo_comment("Stack too deep, try removing local variables.")
|
||||
);
|
||||
while (!stackLayout.empty() && stackLayout.back() != static_cast<int>(stackLayout.size() - 1))
|
||||
if (stackLayout.back() < 0)
|
||||
@@ -842,7 +842,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
StackTooDeepError() <<
|
||||
errinfo_sourceLocation(_inlineAssembly.location()) <<
|
||||
errinfo_comment("Stack too deep, try removing local variables.")
|
||||
util::errinfo_comment("Stack too deep, try removing local variables.")
|
||||
);
|
||||
_assembly.appendInstruction(dupInstruction(stackDiff));
|
||||
}
|
||||
@@ -916,7 +916,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
StackTooDeepError() <<
|
||||
errinfo_sourceLocation(_inlineAssembly.location()) <<
|
||||
errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.")
|
||||
util::errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.")
|
||||
);
|
||||
_assembly.appendInstruction(swapInstruction(stackDiff));
|
||||
_assembly.appendInstruction(Instruction::POP);
|
||||
@@ -1045,7 +1045,7 @@ void ContractCompiler::handleCatch(vector<ASTPointer<TryCatchClause>> const& _ca
|
||||
solAssert(m_context.evmVersion().supportsReturndata(), "");
|
||||
|
||||
// stack: <selector>
|
||||
m_context << Instruction::DUP1 << selectorFromSignature32("Error(string)") << Instruction::EQ;
|
||||
m_context << Instruction::DUP1 << util::selectorFromSignature32("Error(string)") << Instruction::EQ;
|
||||
m_context << Instruction::ISZERO;
|
||||
m_context.appendConditionalJumpTo(panicTag);
|
||||
m_context << Instruction::POP; // remove selector
|
||||
@@ -1077,7 +1077,7 @@ void ContractCompiler::handleCatch(vector<ASTPointer<TryCatchClause>> const& _ca
|
||||
solAssert(m_context.evmVersion().supportsReturndata(), "");
|
||||
|
||||
// stack: <selector>
|
||||
m_context << selectorFromSignature32("Panic(uint256)") << Instruction::EQ;
|
||||
m_context << util::selectorFromSignature32("Panic(uint256)") << Instruction::EQ;
|
||||
m_context << Instruction::ISZERO;
|
||||
m_context.appendConditionalJumpTo(fallbackTag);
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
||||
BOOST_THROW_EXCEPTION(
|
||||
StackTooDeepError() <<
|
||||
errinfo_sourceLocation(_varDecl.location()) <<
|
||||
errinfo_comment("Stack too deep.")
|
||||
util::errinfo_comment("Stack too deep.")
|
||||
);
|
||||
m_context << dupInstruction(retSizeOnStack + 1);
|
||||
m_context.appendJump(evmasm::AssemblyItem::JumpType::OutOfFunction);
|
||||
@@ -350,7 +350,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
StackTooDeepError() <<
|
||||
errinfo_sourceLocation(_assignment.location()) <<
|
||||
errinfo_comment("Stack too deep, try removing local variables.")
|
||||
util::errinfo_comment("Stack too deep, try removing local variables.")
|
||||
);
|
||||
// value [lvalue_ref] updated_value
|
||||
for (unsigned i = 0; i < itemSize; ++i)
|
||||
@@ -1258,6 +1258,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
function.kind() == FunctionType::Kind::ABIEncodeWithSignature;
|
||||
|
||||
TypePointers argumentTypes;
|
||||
TypePointers targetTypes;
|
||||
|
||||
ASTNode::listAccept(arguments, *this);
|
||||
|
||||
@@ -1265,14 +1266,17 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
{
|
||||
solAssert(arguments.size() == 2);
|
||||
|
||||
auto const functionPtr = dynamic_cast<FunctionTypePointer>(arguments[0]->annotation().type);
|
||||
solAssert(functionPtr);
|
||||
|
||||
// Account for tuples with one component which become that component
|
||||
if (auto const tupleType = dynamic_cast<TupleType const*>(arguments[1]->annotation().type))
|
||||
argumentTypes = tupleType->components();
|
||||
else
|
||||
argumentTypes.emplace_back(arguments[1]->annotation().type);
|
||||
|
||||
auto functionPtr = dynamic_cast<FunctionTypePointer>(arguments[0]->annotation().type);
|
||||
solAssert(functionPtr);
|
||||
functionPtr = functionPtr->asExternallyCallableFunction(false);
|
||||
solAssert(functionPtr);
|
||||
targetTypes = functionPtr->parameterTypes();
|
||||
}
|
||||
else
|
||||
for (unsigned i = 0; i < arguments.size(); ++i)
|
||||
@@ -1292,12 +1296,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
if (isPacked)
|
||||
{
|
||||
solAssert(!function.padArguments(), "");
|
||||
utils().packedEncode(argumentTypes, TypePointers());
|
||||
utils().packedEncode(argumentTypes, targetTypes);
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(function.padArguments(), "");
|
||||
utils().abiEncode(argumentTypes, TypePointers());
|
||||
utils().abiEncode(argumentTypes, targetTypes);
|
||||
}
|
||||
utils().fetchFreeMemoryPointer();
|
||||
// stack: [<selector/functionPointer/signature>] <data_encoding_area_end> <bytes_memory_ptr>
|
||||
@@ -1452,7 +1456,7 @@ bool ExpressionCompiler::visit(FunctionCallOptions const& _functionCallOptions)
|
||||
solAssert(false, "Unexpected option name!");
|
||||
acceptAndConvert(*_functionCallOptions.options()[i], *requiredType);
|
||||
|
||||
solAssert(!contains(presentOptions, newOption), "");
|
||||
solAssert(!util::contains(presentOptions, newOption), "");
|
||||
ptrdiff_t insertPos = presentOptions.end() - lower_bound(presentOptions.begin(), presentOptions.end(), newOption);
|
||||
|
||||
utils().moveIntoStack(static_cast<unsigned>(insertPos), 1);
|
||||
@@ -2862,7 +2866,7 @@ void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaratio
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(InternalCompilerError()
|
||||
<< errinfo_sourceLocation(_expression.location())
|
||||
<< errinfo_comment("Identifier type not supported or identifier not found."));
|
||||
<< util::errinfo_comment("Identifier type not supported or identifier not found."));
|
||||
}
|
||||
|
||||
void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression)
|
||||
|
||||
@@ -50,7 +50,7 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
|
||||
BOOST_THROW_EXCEPTION(
|
||||
StackTooDeepError() <<
|
||||
errinfo_sourceLocation(_location) <<
|
||||
errinfo_comment("Stack too deep, try removing local variables.")
|
||||
util::errinfo_comment("Stack too deep, try removing local variables.")
|
||||
);
|
||||
solAssert(stackPos + 1 >= m_size, "Size and stack pos mismatch.");
|
||||
for (unsigned i = 0; i < m_size; ++i)
|
||||
@@ -64,7 +64,7 @@ void StackVariable::storeValue(Type const&, SourceLocation const& _location, boo
|
||||
BOOST_THROW_EXCEPTION(
|
||||
StackTooDeepError() <<
|
||||
errinfo_sourceLocation(_location) <<
|
||||
errinfo_comment("Stack too deep, try removing local variables.")
|
||||
util::errinfo_comment("Stack too deep, try removing local variables.")
|
||||
);
|
||||
else if (stackDiff > 0)
|
||||
for (unsigned i = 0; i < m_size; ++i)
|
||||
@@ -436,7 +436,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
||||
BOOST_THROW_EXCEPTION(
|
||||
InternalCompilerError()
|
||||
<< errinfo_sourceLocation(_location)
|
||||
<< errinfo_comment("Invalid non-value type for assignment."));
|
||||
<< util::errinfo_comment("Invalid non-value type for assignment."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ pair<string, string> IRGenerator::run(
|
||||
map<ContractDefinition const*, string_view const> const& _otherYulSources
|
||||
)
|
||||
{
|
||||
string const ir = yul::reindent(generate(_contract, _cborMetadata, _otherYulSources));
|
||||
string ir = yul::reindent(generate(_contract, _cborMetadata, _otherYulSources));
|
||||
|
||||
yul::AssemblyStack asmStack(
|
||||
m_evmVersion,
|
||||
@@ -113,15 +113,7 @@ pair<string, string> IRGenerator::run(
|
||||
}
|
||||
asmStack.optimize();
|
||||
|
||||
string warning =
|
||||
"/*=====================================================*\n"
|
||||
" * WARNING *\n"
|
||||
" * Solidity to Yul compilation is still EXPERIMENTAL *\n"
|
||||
" * It can result in LOSS OF FUNDS or worse *\n"
|
||||
" * !USE AT YOUR OWN RISK! *\n"
|
||||
" *=====================================================*/\n\n";
|
||||
|
||||
return {warning + ir, warning + asmStack.print(m_context.soliditySourceProvider())};
|
||||
return {move(ir), asmStack.print(m_context.soliditySourceProvider())};
|
||||
}
|
||||
|
||||
string IRGenerator::generate(
|
||||
@@ -234,7 +226,7 @@ string IRGenerator::generate(
|
||||
t("deployedFunctions", m_context.functionCollector().requestedFunctions());
|
||||
t("deployedSubObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||
t("metadataName", yul::Object::metadataName());
|
||||
t("cborMetadata", toHex(_cborMetadata));
|
||||
t("cborMetadata", util::toHex(_cborMetadata));
|
||||
|
||||
t("useSrcMapDeployed", formatUseSrcMap(m_context));
|
||||
|
||||
@@ -788,7 +780,7 @@ pair<string, map<ContractDefinition const*, vector<string>>> IRGenerator::evalua
|
||||
{
|
||||
bool operator()(ContractDefinition const* _c1, ContractDefinition const* _c2) const
|
||||
{
|
||||
solAssert(contains(linearizedBaseContracts, _c1) && contains(linearizedBaseContracts, _c2), "");
|
||||
solAssert(util::contains(linearizedBaseContracts, _c1) && util::contains(linearizedBaseContracts, _c2), "");
|
||||
auto it1 = find(linearizedBaseContracts.begin(), linearizedBaseContracts.end(), _c1);
|
||||
auto it2 = find(linearizedBaseContracts.begin(), linearizedBaseContracts.end(), _c2);
|
||||
return it1 < it2;
|
||||
|
||||
@@ -203,7 +203,7 @@ private:
|
||||
else
|
||||
solAssert(false);
|
||||
|
||||
if (isdigit(value.front()))
|
||||
if (isDigit(value.front()))
|
||||
return yul::Literal{_identifier.debugData, yul::LiteralKind::Number, yul::YulString{value}, {}};
|
||||
else
|
||||
return yul::Identifier{_identifier.debugData, yul::YulString{value}};
|
||||
@@ -1160,10 +1160,22 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
for (auto const& argument: argumentsOfEncodeFunction)
|
||||
{
|
||||
argumentTypes.emplace_back(&type(*argument));
|
||||
targetTypes.emplace_back(type(*argument).fullEncodingType(false, true, isPacked));
|
||||
argumentVars += IRVariable(*argument).stackSlots();
|
||||
}
|
||||
|
||||
if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
|
||||
{
|
||||
auto encodedFunctionType = dynamic_cast<FunctionType const*>(arguments.front()->annotation().type);
|
||||
solAssert(encodedFunctionType);
|
||||
encodedFunctionType = encodedFunctionType->asExternallyCallableFunction(false);
|
||||
solAssert(encodedFunctionType);
|
||||
targetTypes = encodedFunctionType->parameterTypes();
|
||||
}
|
||||
else
|
||||
for (auto const& argument: argumentsOfEncodeFunction)
|
||||
targetTypes.emplace_back(type(*argument).fullEncodingType(false, true, isPacked));
|
||||
|
||||
|
||||
if (functionType->kind() == FunctionType::Kind::ABIEncodeCall)
|
||||
{
|
||||
auto const& selectorType = dynamic_cast<FunctionType const&>(type(*arguments.front()));
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <libsmtutil/CHCSmtLib2Interface.h>
|
||||
#include <liblangutil/CharStreamProvider.h>
|
||||
#include <libsolutil/Algorithms.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
|
||||
#ifdef HAVE_Z3_DLOPEN
|
||||
#include <z3_version.h>
|
||||
@@ -1497,7 +1498,7 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall)
|
||||
|
||||
auto const* contract = function->annotation().contract;
|
||||
auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts;
|
||||
solAssert(kind != FunctionType::Kind::Internal || function->isFree() || (contract && contract->isLibrary()) || contains(hierarchy, contract), "");
|
||||
solAssert(kind != FunctionType::Kind::Internal || function->isFree() || (contract && contract->isLibrary()) || util::contains(hierarchy, contract), "");
|
||||
|
||||
bool usesStaticCall = function->stateMutability() == StateMutability::Pure || function->stateMutability() == StateMutability::View;
|
||||
|
||||
@@ -1998,9 +1999,9 @@ map<unsigned, vector<unsigned>> CHC::summaryCalls(CHCSolverInterface::CexGraph c
|
||||
// Predicates that do not have a CALLID have a predicate id at the end of <suffix>,
|
||||
// so the assertion below should still hold.
|
||||
auto beg = _s.data();
|
||||
while (beg != _s.data() + _s.size() && !isdigit(*beg)) ++beg;
|
||||
while (beg != _s.data() + _s.size() && !isDigit(*beg)) ++beg;
|
||||
auto end = beg;
|
||||
while (end != _s.data() + _s.size() && isdigit(*end)) ++end;
|
||||
while (end != _s.data() + _s.size() && isDigit(*end)) ++end;
|
||||
|
||||
solAssert(beg != end, "Expected to find numerical call or predicate id.");
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ map<Predicate const*, set<string>> collectInvariants(
|
||||
|
||||
map<string, pair<smtutil::Expression, smtutil::Expression>> equalities;
|
||||
// Collect equalities where one of the sides is a predicate we're interested in.
|
||||
BreadthFirstSearch<smtutil::Expression const*>{{&_proof}}.run([&](auto&& _expr, auto&& _addChild) {
|
||||
util::BreadthFirstSearch<smtutil::Expression const*>{{&_proof}}.run([&](auto&& _expr, auto&& _addChild) {
|
||||
if (_expr->name == "=")
|
||||
for (auto const& t: targets)
|
||||
{
|
||||
|
||||
@@ -350,7 +350,7 @@ vector<optional<string>> Predicate::summaryStateValues(vector<smtutil::Expressio
|
||||
|
||||
vector<smtutil::Expression> stateArgs(stateFirst, stateLast);
|
||||
solAssert(stateArgs.size() == stateVars->size(), "");
|
||||
auto stateTypes = applyMap(*stateVars, [&](auto const& _var) { return _var->type(); });
|
||||
auto stateTypes = util::applyMap(*stateVars, [&](auto const& _var) { return _var->type(); });
|
||||
return formatExpressions(stateArgs, stateTypes);
|
||||
}
|
||||
|
||||
@@ -412,7 +412,7 @@ pair<vector<optional<string>>, vector<VariableDeclaration const*>> Predicate::lo
|
||||
auto first = _args.end() - static_cast<int>(localVars.size());
|
||||
vector<smtutil::Expression> outValues(first, _args.end());
|
||||
|
||||
auto mask = applyMap(
|
||||
auto mask = util::applyMap(
|
||||
localVars,
|
||||
[this](auto _var) {
|
||||
auto varScope = dynamic_cast<ScopeOpener const*>(_var->scope());
|
||||
@@ -422,7 +422,7 @@ pair<vector<optional<string>>, vector<VariableDeclaration const*>> Predicate::lo
|
||||
auto localVarsInScope = util::filter(localVars, mask);
|
||||
auto outValuesInScope = util::filter(outValues, mask);
|
||||
|
||||
auto outTypes = applyMap(localVarsInScope, [](auto _var) { return _var->type(); });
|
||||
auto outTypes = util::applyMap(localVarsInScope, [](auto _var) { return _var->type(); });
|
||||
return {formatExpressions(outValuesInScope, outTypes), localVarsInScope};
|
||||
}
|
||||
|
||||
@@ -496,7 +496,7 @@ optional<string> Predicate::expressionToString(smtutil::Expression const& _expr,
|
||||
if (_expr.name == "0")
|
||||
return "0x0";
|
||||
// For some reason the code below returns "0x" for "0".
|
||||
return toHex(toCompactBigEndian(bigint(_expr.name)), HexPrefix::Add, HexCase::Lower);
|
||||
return util::toHex(toCompactBigEndian(bigint(_expr.name)), util::HexPrefix::Add, util::HexCase::Lower);
|
||||
}
|
||||
catch (out_of_range const&)
|
||||
{
|
||||
|
||||
@@ -115,6 +115,12 @@ void SMTEncoder::endVisit(ContractDefinition const& _contract)
|
||||
m_context.popSolver();
|
||||
}
|
||||
|
||||
bool SMTEncoder::visit(ImportDirective const&)
|
||||
{
|
||||
// do not visit because the identifier therein will confuse us.
|
||||
return false;
|
||||
}
|
||||
|
||||
void SMTEncoder::endVisit(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
// State variables are handled by the constructor.
|
||||
@@ -313,7 +319,7 @@ bool SMTEncoder::visit(InlineAssembly const& _inlineAsm)
|
||||
{
|
||||
auto const& vars = _assignment.variableNames;
|
||||
for (auto const& identifier: vars)
|
||||
if (auto externalInfo = valueOrNullptr(externalReferences, &identifier))
|
||||
if (auto externalInfo = util::valueOrNullptr(externalReferences, &identifier))
|
||||
if (auto varDecl = dynamic_cast<VariableDeclaration const*>(externalInfo->declaration))
|
||||
assignedVars.insert(varDecl);
|
||||
}
|
||||
|
||||
@@ -136,6 +136,7 @@ protected:
|
||||
// because the order of expression evaluation is undefined
|
||||
// TODO: or just force a certain order, but people might have a different idea about that.
|
||||
|
||||
bool visit(ImportDirective const& _node) override;
|
||||
bool visit(ContractDefinition const& _node) override;
|
||||
void endVisit(ContractDefinition const& _node) override;
|
||||
void endVisit(VariableDeclaration const& _node) override;
|
||||
|
||||
@@ -211,7 +211,7 @@ void SymbolicState::buildABIFunctions(set<FunctionCall const*> const& _abiFuncti
|
||||
|
||||
|
||||
auto argTypes = [](auto const& _args) {
|
||||
return applyMap(_args, [](auto arg) { return arg->annotation().type; });
|
||||
return util::applyMap(_args, [](auto arg) { return arg->annotation().type; });
|
||||
};
|
||||
|
||||
/// Since each abi.* function may have a different number of input/output parameters,
|
||||
|
||||
@@ -580,7 +580,7 @@ optional<smtutil::Expression> symbolicTypeConversion(frontend::Type const* _from
|
||||
return smtutil::Expression(size_t(0));
|
||||
auto bytesVec = util::asBytes(strType->value());
|
||||
bytesVec.resize(fixedBytesType->numBytes(), 0);
|
||||
return smtutil::Expression(u256(toHex(bytesVec, util::HexPrefix::Add)));
|
||||
return smtutil::Expression(u256(util::toHex(bytesVec, util::HexPrefix::Add)));
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
|
||||
@@ -278,7 +278,7 @@ void CompilerStack::setMetadataHash(MetadataHash _metadataHash)
|
||||
void CompilerStack::selectDebugInfo(DebugInfoSelection _debugInfoSelection)
|
||||
{
|
||||
if (m_stackState >= CompilationSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must select debug info components before compilation."));
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << util::errinfo_comment("Must select debug info components before compilation."));
|
||||
m_debugInfoSelection = _debugInfoSelection;
|
||||
}
|
||||
|
||||
@@ -573,7 +573,7 @@ bool CompilerStack::analyze()
|
||||
if (noErrors)
|
||||
{
|
||||
ModelChecker modelChecker(m_errorReporter, *this, m_smtlib2Responses, m_modelCheckerSettings, m_readFile);
|
||||
auto allSources = applyMap(m_sourceOrder, [](Source const* _source) { return _source->ast; });
|
||||
auto allSources = util::applyMap(m_sourceOrder, [](Source const* _source) { return _source->ast; });
|
||||
modelChecker.enableAllEnginesIfPragmaPresent(allSources);
|
||||
modelChecker.checkRequestedSourcesAndContracts(allSources);
|
||||
for (Source const* source: m_sourceOrder)
|
||||
@@ -1030,7 +1030,7 @@ Json::Value CompilerStack::interfaceSymbols(string const& _contractName) const
|
||||
for (ErrorDefinition const* error: contractDefinition(_contractName).interfaceErrors())
|
||||
{
|
||||
string signature = error->functionType(true)->externalSignature();
|
||||
interfaceSymbols["errors"][signature] = toHex(toCompactBigEndian(selectorFromSignature32(signature), 4));
|
||||
interfaceSymbols["errors"][signature] = util::toHex(toCompactBigEndian(util::selectorFromSignature32(signature), 4));
|
||||
}
|
||||
|
||||
for (EventDefinition const* event: ranges::concat_view(
|
||||
@@ -1040,7 +1040,7 @@ Json::Value CompilerStack::interfaceSymbols(string const& _contractName) const
|
||||
if (!event->isAnonymous())
|
||||
{
|
||||
string signature = event->functionType(true)->externalSignature();
|
||||
interfaceSymbols["events"][signature] = toHex(u256(h256::Arith(keccak256(signature))));
|
||||
interfaceSymbols["events"][signature] = toHex(u256(h256::Arith(util::keccak256(signature))));
|
||||
}
|
||||
|
||||
return interfaceSymbols;
|
||||
@@ -1494,7 +1494,7 @@ string CompilerStack::createMetadata(Contract const& _contract, bool _forIR) con
|
||||
continue;
|
||||
|
||||
solAssert(s.second.charStream, "Character stream not available");
|
||||
meta["sources"][s.first]["keccak256"] = "0x" + toHex(s.second.keccak256().asBytes());
|
||||
meta["sources"][s.first]["keccak256"] = "0x" + util::toHex(s.second.keccak256().asBytes());
|
||||
if (optional<string> licenseString = s.second.ast->licenseString())
|
||||
meta["sources"][s.first]["license"] = *licenseString;
|
||||
if (m_metadataLiteralSources)
|
||||
@@ -1502,7 +1502,7 @@ string CompilerStack::createMetadata(Contract const& _contract, bool _forIR) con
|
||||
else
|
||||
{
|
||||
meta["sources"][s.first]["urls"] = Json::arrayValue;
|
||||
meta["sources"][s.first]["urls"].append("bzz-raw://" + toHex(s.second.swarmHash().asBytes()));
|
||||
meta["sources"][s.first]["urls"].append("bzz-raw://" + util::toHex(s.second.swarmHash().asBytes()));
|
||||
meta["sources"][s.first]["urls"].append(s.second.ipfsUrl());
|
||||
}
|
||||
}
|
||||
@@ -1565,7 +1565,7 @@ string CompilerStack::createMetadata(Contract const& _contract, bool _forIR) con
|
||||
|
||||
meta["settings"]["libraries"] = Json::objectValue;
|
||||
for (auto const& library: m_libraries)
|
||||
meta["settings"]["libraries"][library.first] = "0x" + toHex(library.second.asBytes());
|
||||
meta["settings"]["libraries"][library.first] = "0x" + util::toHex(library.second.asBytes());
|
||||
|
||||
meta["output"]["abi"] = contractABI(_contract);
|
||||
meta["output"]["userdoc"] = natspecUser(_contract);
|
||||
@@ -1677,7 +1677,7 @@ bytes CompilerStack::createCBORMetadata(Contract const& _contract, bool _forIR)
|
||||
else
|
||||
solAssert(m_metadataHash == MetadataHash::None, "Invalid metadata hash");
|
||||
|
||||
if (experimentalMode || _forIR)
|
||||
if (experimentalMode)
|
||||
encoder.pushBool("experimental", true);
|
||||
if (m_metadataFormat == MetadataFormat::WithReleaseVersionTag)
|
||||
encoder.pushBytes("solc", VersionCompactBytes);
|
||||
|
||||
@@ -189,7 +189,7 @@ public:
|
||||
/// Enable EVM Bytecode generation. This is enabled by default.
|
||||
void enableEvmBytecodeGeneration(bool _enable = true) { m_generateEvmBytecode = _enable; }
|
||||
|
||||
/// Enable experimental generation of Yul IR code.
|
||||
/// Enable generation of Yul IR code.
|
||||
void enableIRGeneration(bool _enable = true) { m_generateIR = _enable; }
|
||||
|
||||
/// Enable experimental generation of Ewasm code. If enabled, IR is also generated.
|
||||
@@ -373,8 +373,8 @@ private:
|
||||
std::shared_ptr<evmasm::Assembly> evmRuntimeAssembly;
|
||||
evmasm::LinkerObject object; ///< Deployment object (includes the runtime sub-object).
|
||||
evmasm::LinkerObject runtimeObject; ///< Runtime object.
|
||||
std::string yulIR; ///< Experimental Yul IR code.
|
||||
std::string yulIROptimized; ///< Optimized experimental Yul IR code.
|
||||
std::string yulIR; ///< Yul IR code.
|
||||
std::string yulIROptimized; ///< Optimized Yul IR code.
|
||||
std::string ewasm; ///< Experimental Ewasm text representation
|
||||
evmasm::LinkerObject ewasmObject; ///< Experimental Ewasm code
|
||||
util::LazyInit<std::string const> metadata; ///< The metadata json that will be hashed into the chain.
|
||||
@@ -447,8 +447,7 @@ private:
|
||||
/// Can only be called after state is SourcesSet.
|
||||
Source const& source(std::string const& _sourceName) const;
|
||||
|
||||
/// @param _forIR If true, include a flag that indicates that the bytecode comes from the
|
||||
/// experimental IR codegen.
|
||||
/// @param _forIR If true, include a flag that indicates that the bytecode comes from IR codegen.
|
||||
/// @returns the metadata JSON as a compact string for the given contract.
|
||||
std::string createMetadata(Contract const& _contract, bool _forIR) const;
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ struct OptimiserSettings
|
||||
"xa[rul]" // Prune a bit more in SSA
|
||||
"xa[r]cL" // Turn into SSA again and simplify
|
||||
"gvif" // Run full inliner
|
||||
"CTUca[r]LsTFOtfDnca[r]Iulc" // SSA plus simplify
|
||||
"CTUca[r]LSsTFOtfDnca[r]Iulc" // SSA plus simplify
|
||||
"]"
|
||||
"jmul[jul] VcTOcul jmul"; // Make source short and pretty
|
||||
|
||||
|
||||
@@ -1448,10 +1448,6 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
|
||||
return output;
|
||||
}
|
||||
|
||||
// TODO: move this warning to AssemblyStack
|
||||
output["errors"] = Json::arrayValue;
|
||||
output["errors"].append(formatError(Error::Severity::Warning, "Warning", "general", "Yul is still experimental. Please use the output with care."));
|
||||
|
||||
string contractName = stack.parserResult()->name.str();
|
||||
|
||||
bool const wildcardMatchesExperimental = true;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
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
|
||||
#include <libsolidity/lsp/GotoDefinition.h>
|
||||
#include <libsolidity/lsp/Transport.h> // for RequestError
|
||||
#include <libsolidity/lsp/Utils.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/ASTUtils.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::langutil;
|
||||
using namespace solidity::lsp;
|
||||
using namespace std;
|
||||
|
||||
void GotoDefinition::operator()(MessageID _id, Json::Value const& _args)
|
||||
{
|
||||
auto const [sourceUnitName, lineColumn] = extractSourceUnitNameAndLineColumn(_args);
|
||||
|
||||
ASTNode const* sourceNode = m_server.astNodeAtSourceLocation(sourceUnitName, lineColumn);
|
||||
|
||||
vector<SourceLocation> locations;
|
||||
if (auto const* expression = dynamic_cast<Expression const*>(sourceNode))
|
||||
{
|
||||
// Handles all expressions that can have one or more declaration annotation.
|
||||
if (auto const* declaration = referencedDeclaration(expression))
|
||||
if (auto location = declarationLocation(declaration))
|
||||
locations.emplace_back(move(location.value()));
|
||||
}
|
||||
else if (auto const* identifierPath = dynamic_cast<IdentifierPath const*>(sourceNode))
|
||||
{
|
||||
if (auto const* declaration = identifierPath->annotation().referencedDeclaration)
|
||||
if (auto location = declarationLocation(declaration))
|
||||
locations.emplace_back(move(location.value()));
|
||||
}
|
||||
else if (auto const* importDirective = dynamic_cast<ImportDirective const*>(sourceNode))
|
||||
{
|
||||
auto const& path = *importDirective->annotation().absolutePath;
|
||||
if (fileRepository().sourceUnits().count(path))
|
||||
locations.emplace_back(SourceLocation{0, 0, make_shared<string const>(path)});
|
||||
}
|
||||
|
||||
Json::Value reply = Json::arrayValue;
|
||||
for (SourceLocation const& location: locations)
|
||||
reply.append(toJson(location));
|
||||
client().reply(_id, reply);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
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
|
||||
#include <libsolidity/lsp/HandlerBase.h>
|
||||
|
||||
namespace solidity::lsp
|
||||
{
|
||||
|
||||
class GotoDefinition: public HandlerBase
|
||||
{
|
||||
public:
|
||||
explicit GotoDefinition(LanguageServer& _server): HandlerBase(_server) {}
|
||||
|
||||
void operator()(MessageID, Json::Value const&);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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
|
||||
#include <libsolutil/Exceptions.h>
|
||||
|
||||
#include <libsolidity/lsp/HandlerBase.h>
|
||||
#include <libsolidity/lsp/LanguageServer.h>
|
||||
#include <libsolidity/lsp/Utils.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
using namespace solidity::langutil;
|
||||
using namespace solidity::lsp;
|
||||
using namespace solidity::util;
|
||||
using namespace std;
|
||||
|
||||
Json::Value HandlerBase::toRange(SourceLocation const& _location) const
|
||||
{
|
||||
if (!_location.hasText())
|
||||
return toJsonRange({}, {});
|
||||
|
||||
solAssert(_location.sourceName, "");
|
||||
langutil::CharStream const& stream = charStreamProvider().charStream(*_location.sourceName);
|
||||
LineColumn start = stream.translatePositionToLineColumn(_location.start);
|
||||
LineColumn end = stream.translatePositionToLineColumn(_location.end);
|
||||
return toJsonRange(start, end);
|
||||
}
|
||||
|
||||
Json::Value HandlerBase::toJson(SourceLocation const& _location) const
|
||||
{
|
||||
solAssert(_location.sourceName);
|
||||
Json::Value item = Json::objectValue;
|
||||
item["uri"] = fileRepository().sourceUnitNameToClientPath(*_location.sourceName);
|
||||
item["range"] = toRange(_location);
|
||||
return item;
|
||||
}
|
||||
|
||||
pair<string, LineColumn> HandlerBase::extractSourceUnitNameAndLineColumn(Json::Value const& _args) const
|
||||
{
|
||||
string const uri = _args["textDocument"]["uri"].asString();
|
||||
string const sourceUnitName = fileRepository().clientPathToSourceUnitName(uri);
|
||||
if (!fileRepository().sourceUnits().count(sourceUnitName))
|
||||
BOOST_THROW_EXCEPTION(
|
||||
RequestError(ErrorCode::RequestFailed) <<
|
||||
errinfo_comment("Unknown file: " + uri)
|
||||
);
|
||||
|
||||
auto const lineColumn = parseLineColumn(_args["position"]);
|
||||
if (!lineColumn)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
RequestError(ErrorCode::RequestFailed) <<
|
||||
errinfo_comment(fmt::format(
|
||||
"Unknown position {line}:{column} in file: {file}",
|
||||
fmt::arg("line", lineColumn.value().line),
|
||||
fmt::arg("column", lineColumn.value().column),
|
||||
fmt::arg("file", sourceUnitName)
|
||||
))
|
||||
);
|
||||
|
||||
return {sourceUnitName, *lineColumn};
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
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
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/lsp/FileRepository.h>
|
||||
#include <libsolidity/lsp/LanguageServer.h>
|
||||
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <liblangutil/CharStreamProvider.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace solidity::lsp
|
||||
{
|
||||
|
||||
class Transport;
|
||||
|
||||
/**
|
||||
* Helper base class for implementing handlers.
|
||||
*/
|
||||
class HandlerBase
|
||||
{
|
||||
public:
|
||||
explicit HandlerBase(LanguageServer& _server): m_server{_server} {}
|
||||
|
||||
Json::Value toRange(langutil::SourceLocation const& _location) const;
|
||||
Json::Value toJson(langutil::SourceLocation const& _location) const;
|
||||
|
||||
/// @returns source unit name and the line column position as extracted
|
||||
/// from the JSON-RPC parameters.
|
||||
std::pair<std::string, langutil::LineColumn> extractSourceUnitNameAndLineColumn(Json::Value const& _params) const;
|
||||
|
||||
langutil::CharStreamProvider const& charStreamProvider() const noexcept { return m_server.charStreamProvider(); }
|
||||
FileRepository const& fileRepository() const noexcept { return m_server.fileRepository(); }
|
||||
Transport& client() const noexcept { return m_server.client(); }
|
||||
|
||||
protected:
|
||||
LanguageServer& m_server;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -21,6 +21,11 @@
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
#include <libsolidity/interface/StandardCompiler.h>
|
||||
#include <libsolidity/lsp/LanguageServer.h>
|
||||
#include <libsolidity/lsp/HandlerBase.h>
|
||||
#include <libsolidity/lsp/Utils.h>
|
||||
|
||||
// LSP feature implementations
|
||||
#include <libsolidity/lsp/GotoDefinition.h>
|
||||
|
||||
#include <liblangutil/SourceReferenceExtractor.h>
|
||||
#include <liblangutil/CharStream.h>
|
||||
@@ -48,31 +53,6 @@ using namespace solidity::frontend;
|
||||
namespace
|
||||
{
|
||||
|
||||
Json::Value toJson(LineColumn _pos)
|
||||
{
|
||||
Json::Value json = Json::objectValue;
|
||||
json["line"] = max(_pos.line, 0);
|
||||
json["character"] = max(_pos.column, 0);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
Json::Value toJsonRange(LineColumn const& _start, LineColumn const& _end)
|
||||
{
|
||||
Json::Value json;
|
||||
json["start"] = toJson(_start);
|
||||
json["end"] = toJson(_end);
|
||||
return json;
|
||||
}
|
||||
|
||||
optional<LineColumn> parseLineColumn(Json::Value const& _lineColumn)
|
||||
{
|
||||
if (_lineColumn.isObject() && _lineColumn["line"].isInt() && _lineColumn["character"].isInt())
|
||||
return LineColumn{_lineColumn["line"].asInt(), _lineColumn["character"].asInt()};
|
||||
else
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
int toDiagnosticSeverity(Error::Type _errorType)
|
||||
{
|
||||
// 1=Error, 2=Warning, 3=Info, 4=Hint
|
||||
@@ -97,9 +77,11 @@ LanguageServer::LanguageServer(Transport& _transport):
|
||||
{"initialize", bind(&LanguageServer::handleInitialize, this, _1, _2)},
|
||||
{"initialized", [](auto, auto) {}},
|
||||
{"shutdown", [this](auto, auto) { m_state = State::ShutdownRequested; }},
|
||||
{"textDocument/definition", GotoDefinition(*this) },
|
||||
{"textDocument/didOpen", bind(&LanguageServer::handleTextDocumentDidOpen, this, _2)},
|
||||
{"textDocument/didChange", bind(&LanguageServer::handleTextDocumentDidChange, this, _2)},
|
||||
{"textDocument/didClose", bind(&LanguageServer::handleTextDocumentDidClose, this, _2)},
|
||||
{"textDocument/implementation", GotoDefinition(*this) },
|
||||
{"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _2)},
|
||||
},
|
||||
m_fileRepository("/" /* basePath */),
|
||||
@@ -107,55 +89,14 @@ LanguageServer::LanguageServer(Transport& _transport):
|
||||
{
|
||||
}
|
||||
|
||||
optional<SourceLocation> LanguageServer::parsePosition(
|
||||
string const& _sourceUnitName,
|
||||
Json::Value const& _position
|
||||
) const
|
||||
Json::Value LanguageServer::toRange(SourceLocation const& _location)
|
||||
{
|
||||
if (!m_fileRepository.sourceUnits().count(_sourceUnitName))
|
||||
return nullopt;
|
||||
|
||||
if (optional<LineColumn> lineColumn = parseLineColumn(_position))
|
||||
if (optional<int> const offset = CharStream::translateLineColumnToPosition(
|
||||
m_fileRepository.sourceUnits().at(_sourceUnitName),
|
||||
*lineColumn
|
||||
))
|
||||
return SourceLocation{*offset, *offset, make_shared<string>(_sourceUnitName)};
|
||||
return nullopt;
|
||||
return HandlerBase(*this).toRange(_location);
|
||||
}
|
||||
|
||||
optional<SourceLocation> LanguageServer::parseRange(string const& _sourceUnitName, Json::Value const& _range) const
|
||||
Json::Value LanguageServer::toJson(SourceLocation const& _location)
|
||||
{
|
||||
if (!_range.isObject())
|
||||
return nullopt;
|
||||
optional<SourceLocation> start = parsePosition(_sourceUnitName, _range["start"]);
|
||||
optional<SourceLocation> end = parsePosition(_sourceUnitName, _range["end"]);
|
||||
if (!start || !end)
|
||||
return nullopt;
|
||||
solAssert(*start->sourceName == *end->sourceName);
|
||||
start->end = end->end;
|
||||
return start;
|
||||
}
|
||||
|
||||
Json::Value LanguageServer::toRange(SourceLocation const& _location) const
|
||||
{
|
||||
if (!_location.hasText())
|
||||
return toJsonRange({}, {});
|
||||
|
||||
solAssert(_location.sourceName, "");
|
||||
CharStream const& stream = m_compilerStack.charStream(*_location.sourceName);
|
||||
LineColumn start = stream.translatePositionToLineColumn(_location.start);
|
||||
LineColumn end = stream.translatePositionToLineColumn(_location.end);
|
||||
return toJsonRange(start, end);
|
||||
}
|
||||
|
||||
Json::Value LanguageServer::toJson(SourceLocation const& _location) const
|
||||
{
|
||||
solAssert(_location.sourceName);
|
||||
Json::Value item = Json::objectValue;
|
||||
item["uri"] = m_fileRepository.sourceUnitNameToClientPath(*_location.sourceName);
|
||||
item["range"] = toRange(_location);
|
||||
return item;
|
||||
return HandlerBase(*this).toJson(_location);
|
||||
}
|
||||
|
||||
void LanguageServer::changeConfiguration(Json::Value const& _settings)
|
||||
@@ -253,7 +194,7 @@ bool LanguageServer::run()
|
||||
string const methodName = (*jsonMessage)["method"].asString();
|
||||
id = (*jsonMessage)["id"];
|
||||
|
||||
if (auto handler = valueOrDefault(m_handlers, methodName))
|
||||
if (auto handler = util::valueOrDefault(m_handlers, methodName))
|
||||
handler(id, (*jsonMessage)["params"]);
|
||||
else
|
||||
m_client.error(id, ErrorCode::MethodNotFound, "Unknown method " + methodName);
|
||||
@@ -316,8 +257,10 @@ void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
|
||||
Json::Value replyArgs;
|
||||
replyArgs["serverInfo"]["name"] = "solc";
|
||||
replyArgs["serverInfo"]["version"] = string(VersionNumber);
|
||||
replyArgs["capabilities"]["textDocumentSync"]["openClose"] = true;
|
||||
replyArgs["capabilities"]["definitionProvider"] = true;
|
||||
replyArgs["capabilities"]["implementationProvider"] = true;
|
||||
replyArgs["capabilities"]["textDocumentSync"]["change"] = 2; // 0=none, 1=full, 2=incremental
|
||||
replyArgs["capabilities"]["textDocumentSync"]["openClose"] = true;
|
||||
|
||||
m_client.reply(_id, move(replyArgs));
|
||||
}
|
||||
@@ -371,11 +314,11 @@ void LanguageServer::handleTextDocumentDidChange(Json::Value const& _args)
|
||||
string text = jsonContentChange["text"].asString();
|
||||
if (jsonContentChange["range"].isObject()) // otherwise full content update
|
||||
{
|
||||
optional<SourceLocation> change = parseRange(sourceUnitName, jsonContentChange["range"]);
|
||||
optional<SourceLocation> change = parseRange(m_fileRepository, sourceUnitName, jsonContentChange["range"]);
|
||||
lspAssert(
|
||||
change && change->hasText(),
|
||||
ErrorCode::RequestFailed,
|
||||
"Invalid source range: " + jsonCompactPrint(jsonContentChange["range"])
|
||||
"Invalid source range: " + util::jsonCompactPrint(jsonContentChange["range"])
|
||||
);
|
||||
|
||||
string buffer = m_fileRepository.sourceUnits().at(sourceUnitName);
|
||||
@@ -403,3 +346,19 @@ void LanguageServer::handleTextDocumentDidClose(Json::Value const& _args)
|
||||
|
||||
compileAndUpdateDiagnostics();
|
||||
}
|
||||
|
||||
ASTNode const* LanguageServer::astNodeAtSourceLocation(std::string const& _sourceUnitName, LineColumn const& _filePos)
|
||||
{
|
||||
if (m_compilerStack.state() < CompilerStack::AnalysisPerformed)
|
||||
return nullptr;
|
||||
|
||||
if (!m_fileRepository.sourceUnits().count(_sourceUnitName))
|
||||
return nullptr;
|
||||
|
||||
if (optional<int> sourcePos =
|
||||
m_compilerStack.charStream(_sourceUnitName).translateLineColumnToPosition(_filePos))
|
||||
return locateInnermostASTNode(*sourcePos, m_compilerStack.ast(_sourceUnitName));
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,11 @@ public:
|
||||
/// @return boolean indicating normal or abnormal termination.
|
||||
bool run();
|
||||
|
||||
FileRepository& fileRepository() noexcept { return m_fileRepository; }
|
||||
Transport& client() noexcept { return m_client; }
|
||||
frontend::ASTNode const* astNodeAtSourceLocation(std::string const& _sourceUnitName, langutil::LineColumn const& _filePos);
|
||||
langutil::CharStreamProvider const& charStreamProvider() const noexcept { return m_compilerStack; }
|
||||
|
||||
private:
|
||||
/// Checks if the server is initialized (to be used by messages that need it to be initialized).
|
||||
/// Reports an error and returns false if not.
|
||||
@@ -66,28 +71,19 @@ private:
|
||||
void handleTextDocumentDidOpen(Json::Value const& _args);
|
||||
void handleTextDocumentDidChange(Json::Value const& _args);
|
||||
void handleTextDocumentDidClose(Json::Value const& _args);
|
||||
void handleGotoDefinition(MessageID _id, Json::Value const& _args);
|
||||
|
||||
/// Invoked when the server user-supplied configuration changes (initiated by the client).
|
||||
void changeConfiguration(Json::Value const&);
|
||||
|
||||
/// Compile everything until after analysis phase.
|
||||
void compile();
|
||||
using MessageHandler = std::function<void(MessageID, Json::Value const&)>;
|
||||
|
||||
std::optional<langutil::SourceLocation> parsePosition(
|
||||
std::string const& _sourceUnitName,
|
||||
Json::Value const& _position
|
||||
) const;
|
||||
/// @returns the source location given a source unit name and an LSP Range object,
|
||||
/// or nullopt on failure.
|
||||
std::optional<langutil::SourceLocation> parseRange(
|
||||
std::string const& _sourceUnitName,
|
||||
Json::Value const& _range
|
||||
) const;
|
||||
Json::Value toRange(langutil::SourceLocation const& _location) const;
|
||||
Json::Value toJson(langutil::SourceLocation const& _location) const;
|
||||
Json::Value toRange(langutil::SourceLocation const& _location);
|
||||
Json::Value toJson(langutil::SourceLocation const& _location);
|
||||
|
||||
// LSP related member fields
|
||||
using MessageHandler = std::function<void(MessageID, Json::Value const&)>;
|
||||
|
||||
enum class State { Started, Initialized, ShutdownRequested, ExitRequested, ExitWithoutShutdown };
|
||||
State m_state = State::Started;
|
||||
|
||||
@@ -69,7 +69,7 @@ private:
|
||||
{ \
|
||||
BOOST_THROW_EXCEPTION( \
|
||||
RequestError(errorCode) << \
|
||||
errinfo_comment(errorMessage) \
|
||||
util::errinfo_comment(errorMessage) \
|
||||
); \
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
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
|
||||
|
||||
#include <liblangutil/CharStreamProvider.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/lsp/FileRepository.h>
|
||||
#include <libsolidity/lsp/Utils.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
|
||||
namespace solidity::lsp
|
||||
{
|
||||
|
||||
using namespace frontend;
|
||||
using namespace langutil;
|
||||
using namespace std;
|
||||
|
||||
optional<LineColumn> parseLineColumn(Json::Value const& _lineColumn)
|
||||
{
|
||||
if (_lineColumn.isObject() && _lineColumn["line"].isInt() && _lineColumn["character"].isInt())
|
||||
return LineColumn{_lineColumn["line"].asInt(), _lineColumn["character"].asInt()};
|
||||
else
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
Json::Value toJson(LineColumn const& _pos)
|
||||
{
|
||||
Json::Value json = Json::objectValue;
|
||||
json["line"] = max(_pos.line, 0);
|
||||
json["character"] = max(_pos.column, 0);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
Json::Value toJsonRange(LineColumn const& _start, LineColumn const& _end)
|
||||
{
|
||||
Json::Value json;
|
||||
json["start"] = toJson(_start);
|
||||
json["end"] = toJson(_end);
|
||||
return json;
|
||||
}
|
||||
|
||||
Declaration const* referencedDeclaration(Expression const* _expression)
|
||||
{
|
||||
if (auto const* identifier = dynamic_cast<Identifier const*>(_expression))
|
||||
if (Declaration const* referencedDeclaration = identifier->annotation().referencedDeclaration)
|
||||
return referencedDeclaration;
|
||||
|
||||
if (auto const* memberAccess = dynamic_cast<MemberAccess const*>(_expression))
|
||||
if (memberAccess->annotation().referencedDeclaration)
|
||||
return memberAccess->annotation().referencedDeclaration;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
optional<SourceLocation> declarationLocation(Declaration const* _declaration)
|
||||
{
|
||||
if (!_declaration)
|
||||
return nullopt;
|
||||
|
||||
if (_declaration->nameLocation().isValid())
|
||||
return _declaration->nameLocation();
|
||||
|
||||
if (_declaration->location().isValid())
|
||||
return _declaration->location();
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
optional<SourceLocation> parsePosition(
|
||||
FileRepository const& _fileRepository,
|
||||
string const& _sourceUnitName,
|
||||
Json::Value const& _position
|
||||
)
|
||||
{
|
||||
if (!_fileRepository.sourceUnits().count(_sourceUnitName))
|
||||
return nullopt;
|
||||
|
||||
if (optional<LineColumn> lineColumn = parseLineColumn(_position))
|
||||
if (optional<int> const offset = CharStream::translateLineColumnToPosition(
|
||||
_fileRepository.sourceUnits().at(_sourceUnitName),
|
||||
*lineColumn
|
||||
))
|
||||
return SourceLocation{*offset, *offset, make_shared<string>(_sourceUnitName)};
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
optional<SourceLocation> parseRange(FileRepository const& _fileRepository, string const& _sourceUnitName, Json::Value const& _range)
|
||||
{
|
||||
if (!_range.isObject())
|
||||
return nullopt;
|
||||
optional<SourceLocation> start = parsePosition(_fileRepository, _sourceUnitName, _range["start"]);
|
||||
optional<SourceLocation> end = parsePosition(_fileRepository, _sourceUnitName, _range["end"]);
|
||||
if (!start || !end)
|
||||
return nullopt;
|
||||
solAssert(*start->sourceName == *end->sourceName);
|
||||
start->end = end->end;
|
||||
return start;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
|
||||
#include <libsolutil/JSON.h>
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#include <fstream>
|
||||
#define lspDebug(message) (std::ofstream("/tmp/solc.log", std::ios::app) << (message) << std::endl)
|
||||
#else
|
||||
#define lspDebug(message) do {} while (0)
|
||||
#endif
|
||||
|
||||
namespace solidity::langutil
|
||||
{
|
||||
class CharStreamProvider;
|
||||
}
|
||||
|
||||
namespace solidity::lsp
|
||||
{
|
||||
|
||||
class FileRepository;
|
||||
|
||||
std::optional<langutil::LineColumn> parseLineColumn(Json::Value const& _lineColumn);
|
||||
Json::Value toJson(langutil::LineColumn const& _pos);
|
||||
Json::Value toJsonRange(langutil::LineColumn const& _start, langutil::LineColumn const& _end);
|
||||
|
||||
/// @returns the source location given a source unit name and an LSP Range object,
|
||||
/// or nullopt on failure.
|
||||
std::optional<langutil::SourceLocation> parsePosition(
|
||||
FileRepository const& _fileRepository,
|
||||
std::string const& _sourceUnitName,
|
||||
Json::Value const& _position
|
||||
);
|
||||
|
||||
/// @returns the source location given a source unit name and an LSP Range object,
|
||||
/// or nullopt on failure.
|
||||
std::optional<langutil::SourceLocation> parseRange(
|
||||
FileRepository const& _fileRepository,
|
||||
std::string const& _sourceUnitName,
|
||||
Json::Value const& _range
|
||||
);
|
||||
|
||||
/// Extracts the resolved declaration of the given expression AST node.
|
||||
///
|
||||
/// This may for example be the type declaration of an identifier,
|
||||
/// or the type declaration of a structured member identifier.
|
||||
///
|
||||
/// @returns the resolved type declaration if found, or nullptr otherwise.
|
||||
frontend::Declaration const* referencedDeclaration(frontend::Expression const* _expression);
|
||||
|
||||
/// @returns the location of the declaration's name, if present, or the location of the complete
|
||||
/// declaration otherwise. If the input declaration is nullptr, std::nullopt is returned instead.
|
||||
std::optional<langutil::SourceLocation> declarationLocation(frontend::Declaration const* _declaration);
|
||||
|
||||
}
|
||||
@@ -117,6 +117,9 @@ ASTPointer<SourceUnit> Parser::parse(CharStream& _charStream)
|
||||
case Token::Type:
|
||||
nodes.push_back(parseUserDefinedValueTypeDefinition());
|
||||
break;
|
||||
case Token::Using:
|
||||
nodes.push_back(parseUsingDirective());
|
||||
break;
|
||||
case Token::Function:
|
||||
nodes.push_back(parseFunctionDefinition(true));
|
||||
break;
|
||||
@@ -962,16 +965,37 @@ ASTPointer<UsingForDirective> Parser::parseUsingDirective()
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
|
||||
expectToken(Token::Using);
|
||||
ASTPointer<IdentifierPath> library(parseIdentifierPath());
|
||||
|
||||
vector<ASTPointer<IdentifierPath>> functions;
|
||||
bool const usesBraces = m_scanner->currentToken() == Token::LBrace;
|
||||
if (usesBraces)
|
||||
{
|
||||
do
|
||||
{
|
||||
advance();
|
||||
functions.emplace_back(parseIdentifierPath());
|
||||
}
|
||||
while (m_scanner->currentToken() == Token::Comma);
|
||||
expectToken(Token::RBrace);
|
||||
}
|
||||
else
|
||||
functions.emplace_back(parseIdentifierPath());
|
||||
|
||||
ASTPointer<TypeName> typeName;
|
||||
expectToken(Token::For);
|
||||
if (m_scanner->currentToken() == Token::Mul)
|
||||
advance();
|
||||
else
|
||||
typeName = parseTypeName();
|
||||
bool global = false;
|
||||
if (m_scanner->currentToken() == Token::Identifier && currentLiteral() == "global")
|
||||
{
|
||||
global = true;
|
||||
advance();
|
||||
}
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::Semicolon);
|
||||
return nodeFactory.createNode<UsingForDirective>(library, typeName);
|
||||
return nodeFactory.createNode<UsingForDirective>(move(functions), usesBraces, typeName, global);
|
||||
}
|
||||
|
||||
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
|
||||
|
||||
Reference in New Issue
Block a user