mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #12604 from ethereum/develop
Merge develop into breaking
This commit is contained in:
@@ -25,19 +25,21 @@ using namespace solidity::langutil;
|
||||
using namespace solidity::frontend;
|
||||
using namespace std;
|
||||
|
||||
ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow):
|
||||
ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow, ContractDefinition const* _contract):
|
||||
m_nodeContainer(_nodeContainer),
|
||||
m_currentNode(_functionFlow.entry),
|
||||
m_returnNode(_functionFlow.exit),
|
||||
m_revertNode(_functionFlow.revert),
|
||||
m_transactionReturnNode(_functionFlow.transactionReturn)
|
||||
m_transactionReturnNode(_functionFlow.transactionReturn),
|
||||
m_contract(_contract)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
|
||||
CFG::NodeContainer& _nodeContainer,
|
||||
FunctionDefinition const& _function
|
||||
FunctionDefinition const& _function,
|
||||
ContractDefinition const* _contract
|
||||
)
|
||||
{
|
||||
auto functionFlow = make_unique<FunctionFlow>();
|
||||
@@ -45,7 +47,7 @@ unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
|
||||
functionFlow->exit = _nodeContainer.newNode();
|
||||
functionFlow->revert = _nodeContainer.newNode();
|
||||
functionFlow->transactionReturn = _nodeContainer.newNode();
|
||||
ControlFlowBuilder builder(_nodeContainer, *functionFlow);
|
||||
ControlFlowBuilder builder(_nodeContainer, *functionFlow, _contract);
|
||||
builder.appendControlFlow(_function);
|
||||
|
||||
return functionFlow;
|
||||
@@ -297,7 +299,8 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
|
||||
_functionCall.expression().accept(*this);
|
||||
ASTNode::listAccept(_functionCall.arguments(), *this);
|
||||
|
||||
m_currentNode->functionCalls.emplace_back(&_functionCall);
|
||||
solAssert(!m_currentNode->functionCall);
|
||||
m_currentNode->functionCall = &_functionCall;
|
||||
|
||||
auto nextNode = newLabel();
|
||||
|
||||
@@ -321,8 +324,20 @@ bool ControlFlowBuilder::visit(ModifierInvocation const& _modifierInvocation)
|
||||
auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
|
||||
_modifierInvocation.name().annotation().referencedDeclaration
|
||||
);
|
||||
if (!modifierDefinition) return false;
|
||||
if (!modifierDefinition->isImplemented()) return false;
|
||||
|
||||
if (!modifierDefinition)
|
||||
return false;
|
||||
|
||||
VirtualLookup const& requiredLookup = *_modifierInvocation.name().annotation().requiredLookup;
|
||||
|
||||
if (requiredLookup == VirtualLookup::Virtual)
|
||||
modifierDefinition = &modifierDefinition->resolveVirtual(*m_contract);
|
||||
else
|
||||
solAssert(requiredLookup == VirtualLookup::Static);
|
||||
|
||||
if (!modifierDefinition->isImplemented())
|
||||
return false;
|
||||
|
||||
solAssert(!!m_returnNode, "");
|
||||
|
||||
m_placeholderEntry = newLabel();
|
||||
@@ -355,8 +370,8 @@ bool ControlFlowBuilder::visit(FunctionDefinition const& _functionDefinition)
|
||||
|
||||
}
|
||||
|
||||
for (auto const& modifier: _functionDefinition.modifiers())
|
||||
appendControlFlow(*modifier);
|
||||
for (auto const& modifierInvocation: _functionDefinition.modifiers())
|
||||
appendControlFlow(*modifierInvocation);
|
||||
|
||||
appendControlFlow(_functionDefinition.body());
|
||||
|
||||
|
||||
@@ -37,13 +37,15 @@ class ControlFlowBuilder: private ASTConstVisitor, private yul::ASTWalker
|
||||
public:
|
||||
static std::unique_ptr<FunctionFlow> createFunctionFlow(
|
||||
CFG::NodeContainer& _nodeContainer,
|
||||
FunctionDefinition const& _function
|
||||
FunctionDefinition const& _function,
|
||||
ContractDefinition const* _contract = nullptr
|
||||
);
|
||||
|
||||
private:
|
||||
explicit ControlFlowBuilder(
|
||||
CFG::NodeContainer& _nodeContainer,
|
||||
FunctionFlow const& _functionFlow
|
||||
FunctionFlow const& _functionFlow,
|
||||
ContractDefinition const* _contract = nullptr
|
||||
);
|
||||
|
||||
// Visits for constructing the control flow.
|
||||
@@ -158,6 +160,8 @@ private:
|
||||
CFGNode* m_revertNode = nullptr;
|
||||
CFGNode* m_transactionReturnNode = nullptr;
|
||||
|
||||
ContractDefinition const* m_contract = nullptr;
|
||||
|
||||
/// The current jump destination of break Statements.
|
||||
CFGNode* m_breakJump = nullptr;
|
||||
/// The current jump destination of continue Statements.
|
||||
|
||||
@@ -44,7 +44,7 @@ bool CFG::visit(ContractDefinition const& _contract)
|
||||
for (FunctionDefinition const* function: contract->definedFunctions())
|
||||
if (function->isImplemented())
|
||||
m_functionControlFlow[{&_contract, function}] =
|
||||
ControlFlowBuilder::createFunctionFlow(m_nodeContainer, *function);
|
||||
ControlFlowBuilder::createFunctionFlow(m_nodeContainer, *function, &_contract);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -98,8 +98,8 @@ struct CFGNode
|
||||
std::vector<CFGNode*> entries;
|
||||
/// Exit nodes. All CFG nodes to which control flow may continue after this node.
|
||||
std::vector<CFGNode*> exits;
|
||||
/// Function calls done by this node
|
||||
std::vector<FunctionCall const*> functionCalls;
|
||||
/// Function call done by this node
|
||||
FunctionCall const* functionCall = nullptr;
|
||||
|
||||
/// Variable occurrences in the node.
|
||||
std::vector<VariableOccurrence> variableOccurrences;
|
||||
|
||||
@@ -81,27 +81,27 @@ void ControlFlowRevertPruner::findRevertStates()
|
||||
if (_node == functionFlow.exit)
|
||||
foundExit = true;
|
||||
|
||||
for (auto const* functionCall: _node->functionCalls)
|
||||
if (auto const* functionCall = _node->functionCall)
|
||||
{
|
||||
auto const* resolvedFunction = ASTNode::resolveFunctionCall(*functionCall, item.contract);
|
||||
|
||||
if (resolvedFunction == nullptr || !resolvedFunction->isImplemented())
|
||||
continue;
|
||||
|
||||
CFG::FunctionContractTuple calledFunctionTuple{
|
||||
findScopeContract(*resolvedFunction, item.contract),
|
||||
resolvedFunction
|
||||
};
|
||||
switch (m_functions.at(calledFunctionTuple))
|
||||
if (resolvedFunction && resolvedFunction->isImplemented())
|
||||
{
|
||||
case RevertState::Unknown:
|
||||
wakeUp[calledFunctionTuple].insert(item);
|
||||
foundUnknown = true;
|
||||
return;
|
||||
case RevertState::AllPathsRevert:
|
||||
return;
|
||||
case RevertState::HasNonRevertingPath:
|
||||
break;
|
||||
CFG::FunctionContractTuple calledFunctionTuple{
|
||||
findScopeContract(*resolvedFunction, item.contract),
|
||||
resolvedFunction
|
||||
};
|
||||
switch (m_functions.at(calledFunctionTuple))
|
||||
{
|
||||
case RevertState::Unknown:
|
||||
wakeUp[calledFunctionTuple].insert(item);
|
||||
foundUnknown = true;
|
||||
return;
|
||||
case RevertState::AllPathsRevert:
|
||||
return;
|
||||
case RevertState::HasNonRevertingPath:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,31 +135,29 @@ void ControlFlowRevertPruner::modifyFunctionFlows()
|
||||
FunctionFlow const& functionFlow = m_cfg.functionFlow(*item.first.function, item.first.contract);
|
||||
solidity::util::BreadthFirstSearch<CFGNode*>{{functionFlow.entry}}.run(
|
||||
[&](CFGNode* _node, auto&& _addChild) {
|
||||
for (auto const* functionCall: _node->functionCalls)
|
||||
if (auto const* functionCall = _node->functionCall)
|
||||
{
|
||||
auto const* resolvedFunction = ASTNode::resolveFunctionCall(*functionCall, item.first.contract);
|
||||
|
||||
if (resolvedFunction == nullptr || !resolvedFunction->isImplemented())
|
||||
continue;
|
||||
if (resolvedFunction && resolvedFunction->isImplemented())
|
||||
switch (m_functions.at({findScopeContract(*resolvedFunction, item.first.contract), resolvedFunction}))
|
||||
{
|
||||
case RevertState::Unknown:
|
||||
[[fallthrough]];
|
||||
case RevertState::AllPathsRevert:
|
||||
// If the revert states of the functions do not
|
||||
// change anymore, we treat all "unknown" states as
|
||||
// "reverting", since they can only be caused by
|
||||
// recursion.
|
||||
for (CFGNode * node: _node->exits)
|
||||
ranges::remove(node->entries, _node);
|
||||
|
||||
switch (m_functions.at({findScopeContract(*resolvedFunction, item.first.contract), resolvedFunction}))
|
||||
{
|
||||
case RevertState::Unknown:
|
||||
[[fallthrough]];
|
||||
case RevertState::AllPathsRevert:
|
||||
// If the revert states of the functions do not
|
||||
// change anymore, we treat all "unknown" states as
|
||||
// "reverting", since they can only be caused by
|
||||
// recursion.
|
||||
for (CFGNode * node: _node->exits)
|
||||
ranges::remove(node->entries, _node);
|
||||
|
||||
_node->exits = {functionFlow.revert};
|
||||
functionFlow.revert->entries.push_back(_node);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_node->exits = {functionFlow.revert};
|
||||
functionFlow.revert->entries.push_back(_node);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (CFGNode* exit: _node->exits)
|
||||
|
||||
@@ -437,16 +437,14 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
||||
type = TypeProvider::withLocation(ref, typeLoc, isPointer);
|
||||
}
|
||||
|
||||
if (
|
||||
_variable.isConstant() &&
|
||||
!dynamic_cast<UserDefinedValueType const*>(type) &&
|
||||
type->containsNestedMapping()
|
||||
)
|
||||
m_errorReporter.fatalDeclarationError(
|
||||
3530_error,
|
||||
_variable.location(),
|
||||
"The type contains a (nested) mapping and therefore cannot be a constant."
|
||||
);
|
||||
if (_variable.isConstant() && !type->isValueType())
|
||||
{
|
||||
bool allowed = false;
|
||||
if (auto arrayType = dynamic_cast<ArrayType const*>(type))
|
||||
allowed = arrayType->isByteArray();
|
||||
if (!allowed)
|
||||
m_errorReporter.fatalTypeError(9259_error, _variable.location(), "Only constants of value type and byte array type are implemented.");
|
||||
}
|
||||
|
||||
_variable.annotation().type = type;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <libsolidity/analysis/DocStringAnalyser.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
@@ -37,7 +38,7 @@ using namespace solidity::frontend;
|
||||
namespace
|
||||
{
|
||||
|
||||
void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, StructurallyDocumentedAnnotation& _target, CallableDeclaration const* _declaration = nullptr)
|
||||
void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, StructurallyDocumentedAnnotation& _target, FunctionType const* _functionType = nullptr)
|
||||
{
|
||||
// Only copy if there is exactly one direct base function.
|
||||
if (_baseFunctions.size() != 1)
|
||||
@@ -45,12 +46,6 @@ void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, Stru
|
||||
|
||||
CallableDeclaration const& baseFunction = **_baseFunctions.begin();
|
||||
|
||||
auto hasReturnParameter = [](CallableDeclaration const& declaration, size_t _n)
|
||||
{
|
||||
return declaration.returnParameterList() &&
|
||||
declaration.returnParameters().size() > _n;
|
||||
};
|
||||
|
||||
auto& sourceDoc = dynamic_cast<StructurallyDocumentedAnnotation const&>(baseFunction.annotation());
|
||||
|
||||
for (auto it = sourceDoc.docTags.begin(); it != sourceDoc.docTags.end();)
|
||||
@@ -70,21 +65,22 @@ void copyMissingTags(set<CallableDeclaration const*> const& _baseFunctions, Stru
|
||||
DocTag content = it->second;
|
||||
|
||||
// Update the parameter name for @return tags
|
||||
if (_declaration && tag == "return")
|
||||
if (_functionType && tag == "return")
|
||||
{
|
||||
size_t docParaNameEndPos = content.content.find_first_of(" \t");
|
||||
string const docParameterName = content.content.substr(0, docParaNameEndPos);
|
||||
|
||||
if (
|
||||
hasReturnParameter(*_declaration, n) &&
|
||||
docParameterName != _declaration->returnParameters().at(n)->name()
|
||||
_functionType->returnParameterNames().size() > n &&
|
||||
docParameterName != _functionType->returnParameterNames().at(n)
|
||||
)
|
||||
{
|
||||
bool baseHasNoName =
|
||||
hasReturnParameter(baseFunction, n) &&
|
||||
baseFunction.returnParameterList() &&
|
||||
baseFunction.returnParameters().size() > n &&
|
||||
baseFunction.returnParameters().at(n)->name().empty();
|
||||
|
||||
string paramName = _declaration->returnParameters().at(n)->name();
|
||||
string paramName = _functionType->returnParameterNames().at(n);
|
||||
content.content =
|
||||
(paramName.empty() ? "" : std::move(paramName) + " ") + (
|
||||
string::npos == docParaNameEndPos || baseHasNoName ?
|
||||
@@ -127,7 +123,7 @@ bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit)
|
||||
bool DocStringAnalyser::visit(FunctionDefinition const& _function)
|
||||
{
|
||||
if (!_function.isConstructor())
|
||||
handleCallable(_function, _function, _function.annotation());
|
||||
handleCallable(_function, _function, _function.annotation(), TypeProvider::function(_function));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -136,10 +132,12 @@ bool DocStringAnalyser::visit(VariableDeclaration const& _variable)
|
||||
if (!_variable.isStateVariable() && !_variable.isFileLevelVariable())
|
||||
return false;
|
||||
|
||||
auto const* getterType = TypeProvider::function(_variable);
|
||||
|
||||
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_variable.annotation().baseFunctions, _variable, _variable.annotation()))
|
||||
copyMissingTags({baseFunction}, _variable.annotation());
|
||||
copyMissingTags({baseFunction}, _variable.annotation(), getterType);
|
||||
else if (_variable.annotation().docTags.empty())
|
||||
copyMissingTags(_variable.annotation().baseFunctions, _variable.annotation());
|
||||
copyMissingTags(_variable.annotation().baseFunctions, _variable.annotation(), getterType);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -168,17 +166,18 @@ bool DocStringAnalyser::visit(ErrorDefinition const& _error)
|
||||
void DocStringAnalyser::handleCallable(
|
||||
CallableDeclaration const& _callable,
|
||||
StructurallyDocumented const& _node,
|
||||
StructurallyDocumentedAnnotation& _annotation
|
||||
StructurallyDocumentedAnnotation& _annotation,
|
||||
FunctionType const* _functionType
|
||||
)
|
||||
{
|
||||
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_callable.annotation().baseFunctions, _node, _annotation))
|
||||
copyMissingTags({baseFunction}, _annotation, &_callable);
|
||||
copyMissingTags({baseFunction}, _annotation, _functionType);
|
||||
else if (
|
||||
_annotation.docTags.empty() &&
|
||||
_callable.annotation().baseFunctions.size() == 1 &&
|
||||
parameterNamesEqual(_callable, **_callable.annotation().baseFunctions.begin())
|
||||
)
|
||||
copyMissingTags(_callable.annotation().baseFunctions, _annotation, &_callable);
|
||||
copyMissingTags(_callable.annotation().baseFunctions, _annotation, _functionType);
|
||||
}
|
||||
|
||||
CallableDeclaration const* DocStringAnalyser::resolveInheritDoc(
|
||||
|
||||
@@ -54,7 +54,8 @@ private:
|
||||
void handleCallable(
|
||||
CallableDeclaration const& _callable,
|
||||
StructurallyDocumented const& _node,
|
||||
StructurallyDocumentedAnnotation& _annotation
|
||||
StructurallyDocumentedAnnotation& _annotation,
|
||||
FunctionType const* _functionType = nullptr
|
||||
);
|
||||
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
|
||||
@@ -73,24 +73,24 @@ inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariable
|
||||
|
||||
return {
|
||||
magicVarDecl("abi", TypeProvider::magic(MagicType::Kind::ABI)),
|
||||
magicVarDecl("addmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)),
|
||||
magicVarDecl("assert", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)),
|
||||
magicVarDecl("addmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, StateMutability::Pure)),
|
||||
magicVarDecl("assert", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Assert, StateMutability::Pure)),
|
||||
magicVarDecl("block", TypeProvider::magic(MagicType::Kind::Block)),
|
||||
magicVarDecl("blockhash", TypeProvider::function(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)),
|
||||
magicVarDecl("ecrecover", TypeProvider::function(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)),
|
||||
magicVarDecl("gasleft", TypeProvider::function(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)),
|
||||
magicVarDecl("keccak256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
|
||||
magicVarDecl("blockhash", TypeProvider::function(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, StateMutability::View)),
|
||||
magicVarDecl("ecrecover", TypeProvider::function(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, StateMutability::Pure)),
|
||||
magicVarDecl("gasleft", TypeProvider::function(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, StateMutability::View)),
|
||||
magicVarDecl("keccak256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, StateMutability::Pure)),
|
||||
magicVarDecl("msg", TypeProvider::magic(MagicType::Kind::Message)),
|
||||
magicVarDecl("mulmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, false, StateMutability::Pure)),
|
||||
magicVarDecl("mulmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, StateMutability::Pure)),
|
||||
magicVarDecl("now", TypeProvider::uint256()),
|
||||
magicVarDecl("require", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)),
|
||||
magicVarDecl("require", TypeProvider::function(strings{"bool", "string memory"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)),
|
||||
magicVarDecl("revert", TypeProvider::function(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
|
||||
magicVarDecl("revert", TypeProvider::function(strings{"string memory"}, strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
|
||||
magicVarDecl("ripemd160", TypeProvider::function(strings{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, false, StateMutability::Pure)),
|
||||
magicVarDecl("require", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Require, StateMutability::Pure)),
|
||||
magicVarDecl("require", TypeProvider::function(strings{"bool", "string memory"}, strings{}, FunctionType::Kind::Require, StateMutability::Pure)),
|
||||
magicVarDecl("revert", TypeProvider::function(strings(), strings(), FunctionType::Kind::Revert, StateMutability::Pure)),
|
||||
magicVarDecl("revert", TypeProvider::function(strings{"string memory"}, strings(), FunctionType::Kind::Revert, StateMutability::Pure)),
|
||||
magicVarDecl("ripemd160", TypeProvider::function(strings{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, StateMutability::Pure)),
|
||||
magicVarDecl("selfdestruct", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
|
||||
magicVarDecl("sha256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)),
|
||||
magicVarDecl("sha3", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
|
||||
magicVarDecl("sha256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, StateMutability::Pure)),
|
||||
magicVarDecl("sha3", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, StateMutability::Pure)),
|
||||
magicVarDecl("suicide", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
|
||||
magicVarDecl("tx", TypeProvider::magic(MagicType::Kind::Transaction)),
|
||||
// Accepts a MagicType that can be any contract type or an Integer type and returns a
|
||||
@@ -99,8 +99,8 @@ inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariable
|
||||
strings{},
|
||||
strings{},
|
||||
FunctionType::Kind::MetaType,
|
||||
true,
|
||||
StateMutability::Pure
|
||||
StateMutability::Pure,
|
||||
FunctionType::Options::withArbitraryParameters()
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ void ImmutableValidator::analyze()
|
||||
{
|
||||
m_inCreationContext = true;
|
||||
|
||||
auto linearizedContracts = m_currentContract.annotation().linearizedBaseContracts | ranges::views::reverse;
|
||||
auto linearizedContracts = m_mostDerivedContract.annotation().linearizedBaseContracts | ranges::views::reverse;
|
||||
|
||||
for (ContractDefinition const* contract: linearizedContracts)
|
||||
for (VariableDeclaration const* stateVar: contract->stateVariables())
|
||||
@@ -62,7 +62,7 @@ void ImmutableValidator::analyze()
|
||||
visitCallableIfNew(*modDef);
|
||||
}
|
||||
|
||||
checkAllVariablesInitialized(m_currentContract.location());
|
||||
checkAllVariablesInitialized(m_mostDerivedContract.location());
|
||||
}
|
||||
|
||||
bool ImmutableValidator::visit(Assignment const& _assignment)
|
||||
@@ -137,7 +137,7 @@ void ImmutableValidator::endVisit(IdentifierPath const& _identifierPath)
|
||||
if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifierPath.annotation().referencedDeclaration))
|
||||
visitCallableIfNew(
|
||||
*_identifierPath.annotation().requiredLookup == VirtualLookup::Virtual ?
|
||||
callableDef->resolveVirtual(m_currentContract) :
|
||||
callableDef->resolveVirtual(m_mostDerivedContract) :
|
||||
*callableDef
|
||||
);
|
||||
|
||||
@@ -147,7 +147,7 @@ void ImmutableValidator::endVisit(IdentifierPath const& _identifierPath)
|
||||
void ImmutableValidator::endVisit(Identifier const& _identifier)
|
||||
{
|
||||
if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
||||
visitCallableIfNew(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual ? callableDef->resolveVirtual(m_currentContract) : *callableDef);
|
||||
visitCallableIfNew(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual ? callableDef->resolveVirtual(m_mostDerivedContract) : *callableDef);
|
||||
if (auto const varDecl = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
||||
analyseVariableReference(*varDecl, _identifier);
|
||||
}
|
||||
@@ -160,15 +160,18 @@ void ImmutableValidator::endVisit(Return const& _return)
|
||||
|
||||
bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDeclaration)
|
||||
{
|
||||
FunctionDefinition const* prevConstructor = m_currentConstructor;
|
||||
m_currentConstructor = nullptr;
|
||||
ScopedSaveAndRestore constructorGuard{m_currentConstructor, {}};
|
||||
ScopedSaveAndRestore constructorContractGuard{m_currentConstructorContract, {}};
|
||||
|
||||
if (FunctionDefinition const* funcDef = dynamic_cast<decltype(funcDef)>(&_callableDeclaration))
|
||||
{
|
||||
ASTNode::listAccept(funcDef->modifiers(), *this);
|
||||
|
||||
if (funcDef->isConstructor())
|
||||
{
|
||||
m_currentConstructorContract = funcDef->annotation().contract;
|
||||
m_currentConstructor = funcDef;
|
||||
}
|
||||
|
||||
if (funcDef->isImplemented())
|
||||
funcDef->body().accept(*this);
|
||||
@@ -177,8 +180,6 @@ bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDec
|
||||
if (modDef->isImplemented())
|
||||
modDef->body().accept(*this);
|
||||
|
||||
m_currentConstructor = prevConstructor;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -253,7 +254,8 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va
|
||||
|
||||
void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::SourceLocation const& _location)
|
||||
{
|
||||
for (ContractDefinition const* contract: m_currentContract.annotation().linearizedBaseContracts)
|
||||
for (ContractDefinition const* contract: m_mostDerivedContract.annotation().linearizedBaseContracts | ranges::views::reverse)
|
||||
{
|
||||
for (VariableDeclaration const* varDecl: contract->stateVariables())
|
||||
if (varDecl->immutable())
|
||||
if (!util::contains(m_initializedStateVariables, varDecl))
|
||||
@@ -263,6 +265,11 @@ void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::Source
|
||||
solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()),
|
||||
"Construction control flow ends without initializing all immutable state variables."
|
||||
);
|
||||
|
||||
// Don't check further than the current c'tors contract
|
||||
if (contract == m_currentConstructorContract)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ImmutableValidator::visitCallableIfNew(Declaration const& _declaration)
|
||||
|
||||
@@ -42,7 +42,7 @@ class ImmutableValidator: private ASTConstVisitor
|
||||
|
||||
public:
|
||||
ImmutableValidator(langutil::ErrorReporter& _errorReporter, ContractDefinition const& _contractDefinition):
|
||||
m_currentContract(_contractDefinition),
|
||||
m_mostDerivedContract(_contractDefinition),
|
||||
m_errorReporter(_errorReporter)
|
||||
{ }
|
||||
|
||||
@@ -66,7 +66,7 @@ private:
|
||||
|
||||
void visitCallableIfNew(Declaration const& _declaration);
|
||||
|
||||
ContractDefinition const& m_currentContract;
|
||||
ContractDefinition const& m_mostDerivedContract;
|
||||
|
||||
CallableDeclarationSet m_visitedCallables;
|
||||
|
||||
@@ -74,6 +74,7 @@ private:
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
|
||||
FunctionDefinition const* m_currentConstructor = nullptr;
|
||||
ContractDefinition const* m_currentConstructorContract = nullptr;
|
||||
bool m_inLoop = false;
|
||||
bool m_inBranch = false;
|
||||
bool m_inCreationContext = true;
|
||||
|
||||
@@ -571,7 +571,8 @@ bool DeclarationRegistrationHelper::visit(ImportDirective& _import)
|
||||
if (!m_scopes[importee])
|
||||
m_scopes[importee] = make_shared<DeclarationContainer>(nullptr, m_scopes[nullptr].get());
|
||||
m_scopes[&_import] = m_scopes[importee];
|
||||
return ASTVisitor::visit(_import);
|
||||
ASTVisitor::visit(_import);
|
||||
return false; // Do not recurse into child nodes (Identifier for symbolAliases)
|
||||
}
|
||||
|
||||
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
|
||||
|
||||
@@ -530,15 +530,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
||||
}
|
||||
if (_variable.isConstant())
|
||||
{
|
||||
if (!varType->isValueType())
|
||||
{
|
||||
bool allowed = false;
|
||||
if (auto arrayType = dynamic_cast<ArrayType const*>(varType))
|
||||
allowed = arrayType->isByteArray();
|
||||
if (!allowed)
|
||||
m_errorReporter.fatalTypeError(9259_error, _variable.location(), "Constants of non-value type not yet implemented.");
|
||||
}
|
||||
|
||||
if (!_variable.value())
|
||||
m_errorReporter.typeError(4266_error, _variable.location(), "Uninitialized \"constant\" variable.");
|
||||
else if (!*_variable.value()->annotation().isPure)
|
||||
@@ -2122,16 +2113,35 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
|
||||
return;
|
||||
}
|
||||
|
||||
if (functionPointerType->kind() != FunctionType::Kind::External)
|
||||
if (
|
||||
functionPointerType->kind() != FunctionType::Kind::External &&
|
||||
functionPointerType->kind() != FunctionType::Kind::Declaration
|
||||
)
|
||||
{
|
||||
string msg = "Function must be \"public\" or \"external\".";
|
||||
string msg = "Expected regular external function type, or external view on public function.";
|
||||
if (functionPointerType->kind() == FunctionType::Kind::Internal)
|
||||
msg += " Provided internal function.";
|
||||
else if (functionPointerType->kind() == FunctionType::Kind::DelegateCall)
|
||||
msg += " Cannot use library functions for abi.encodeCall.";
|
||||
else if (functionPointerType->kind() == FunctionType::Kind::Creation)
|
||||
msg += " Provided creation function.";
|
||||
else
|
||||
msg += " Cannot use special function.";
|
||||
SecondarySourceLocation ssl{};
|
||||
|
||||
if (functionPointerType->hasDeclaration())
|
||||
{
|
||||
ssl.append("Function is declared here:", functionPointerType->declaration().location());
|
||||
if (functionPointerType->declaration().scope() == m_currentContract)
|
||||
if (
|
||||
functionPointerType->declaration().visibility() == Visibility::Public &&
|
||||
functionPointerType->declaration().scope() == m_currentContract
|
||||
)
|
||||
msg += " Did you forget to prefix \"this.\"?";
|
||||
else if (contains(
|
||||
m_currentContract->annotation().linearizedBaseContracts,
|
||||
functionPointerType->declaration().scope()
|
||||
) && functionPointerType->declaration().scope() != m_currentContract)
|
||||
msg += " Functions from base contracts have to be external.";
|
||||
}
|
||||
|
||||
m_errorReporter.typeError(3509_error, arguments[0]->location(), ssl, msg);
|
||||
@@ -2866,7 +2876,6 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
|
||||
strings(1, ""),
|
||||
strings(1, ""),
|
||||
FunctionType::Kind::ObjectCreation,
|
||||
false,
|
||||
StateMutability::Pure
|
||||
);
|
||||
_newExpression.annotation().isPure = true;
|
||||
|
||||
Reference in New Issue
Block a user