mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
commit
908b46e9a7
@ -1,6 +1,7 @@
|
||||
### 0.4.21 (unreleased)
|
||||
|
||||
Features:
|
||||
* C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature.
|
||||
* Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature.
|
||||
* Standard JSON: Reject badly formatted invalid JSON inputs.
|
||||
* Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature.
|
||||
|
@ -324,7 +324,8 @@ is ``false``. The default value for the ``uint`` or ``int`` types is ``0``. For
|
||||
element will be initialized to the default value corresponding to its type. Finally, for dynamically-sized arrays, ``bytes``
|
||||
and ``string``, the default value is an empty array or string.
|
||||
|
||||
A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared.
|
||||
A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared
|
||||
(this will change soon, see below).
|
||||
This happens because Solidity inherits its scoping rules from JavaScript.
|
||||
This is in contrast to many languages where variables are only scoped where they are declared until the end of the semantic block.
|
||||
As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``::
|
||||
@ -366,7 +367,9 @@ As a result, the following code is illegal and cause the compiler to throw an er
|
||||
}
|
||||
|
||||
In addition to this, if a variable is declared, it will be initialized at the beginning of the function to its default value.
|
||||
As a result, the following code is legal, despite being poorly written::
|
||||
As a result, the following code is legal, despite being poorly written:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
@ -383,6 +386,60 @@ As a result, the following code is legal, despite being poorly written::
|
||||
}
|
||||
}
|
||||
|
||||
Scoping starting from Version 0.5.0
|
||||
-----------------------------------
|
||||
|
||||
Starting from version 0.5.0, Solidity will change to the more widespread scoping rules of C99
|
||||
(and many other languages): Variables are visible from the point right after their declaration
|
||||
until the end of a ``{ }``-block. As an exception to this rule, variables declared in the
|
||||
initialization part of a for-loop are only visible until the end of the for-loop.
|
||||
|
||||
Variables and other items declared outside of a code block, for example functions, contracts,
|
||||
user-defined types, etc., do not change their scoping behaviour. This means you can
|
||||
use state variables before they are declared and call functions recursively.
|
||||
|
||||
These rules are already introduced now as an experimental feature.
|
||||
|
||||
As a consequence, the following examples will compile without warnings, since
|
||||
the two variables have the same name but disjoint scopes. In non-0.5.0-mode,
|
||||
they have the same scope (the function ``minimalScoping``) and thus it does
|
||||
not compile there.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma experimental "v0.5.0";
|
||||
contract C {
|
||||
function minimalScoping() pure public {
|
||||
{
|
||||
uint same2 = 0;
|
||||
}
|
||||
|
||||
{
|
||||
uint same2 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
As a special example of the C99 scoping rules, note that in the following,
|
||||
the first assignment to ``x`` will actually assign the outer and not the inner variable.
|
||||
In any case, you will get a warning about the outer variable being shadowed.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma experimental "v0.5.0";
|
||||
contract C {
|
||||
function f() pure public returns (uint) {
|
||||
uint x = 1;
|
||||
{
|
||||
x = 2; // this will assign to the outer variable
|
||||
uint x;
|
||||
}
|
||||
return x; // x has value 2
|
||||
}
|
||||
}
|
||||
|
||||
.. index:: ! exception, ! throw, ! assert, ! require, ! revert
|
||||
|
||||
Error handling: Assert, Require, Revert and Exceptions
|
||||
|
@ -79,6 +79,17 @@ Declaration const* DeclarationContainer::conflictingDeclaration(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DeclarationContainer::activateVariable(ASTString const& _name)
|
||||
{
|
||||
solAssert(
|
||||
m_invisibleDeclarations.count(_name) && m_invisibleDeclarations.at(_name).size() == 1,
|
||||
"Tried to activate a non-inactive variable or multiple inactive variables with the same name."
|
||||
);
|
||||
solAssert(m_declarations.count(_name) == 0 || m_declarations.at(_name).empty(), "");
|
||||
m_declarations[_name].emplace_back(m_invisibleDeclarations.at(_name).front());
|
||||
m_invisibleDeclarations.erase(_name);
|
||||
}
|
||||
|
||||
bool DeclarationContainer::registerDeclaration(
|
||||
Declaration const& _declaration,
|
||||
ASTString const* _name,
|
||||
@ -106,15 +117,17 @@ bool DeclarationContainer::registerDeclaration(
|
||||
return true;
|
||||
}
|
||||
|
||||
vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
|
||||
vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive, bool _alsoInvisible) const
|
||||
{
|
||||
solAssert(!_name.empty(), "Attempt to resolve empty name.");
|
||||
auto result = m_declarations.find(_name);
|
||||
if (result != m_declarations.end())
|
||||
return result->second;
|
||||
if (_recursive && m_enclosingContainer)
|
||||
return m_enclosingContainer->resolveName(_name, true);
|
||||
return vector<Declaration const*>({});
|
||||
vector<Declaration const*> result;
|
||||
if (m_declarations.count(_name))
|
||||
result = m_declarations.at(_name);
|
||||
if (_alsoInvisible && m_invisibleDeclarations.count(_name))
|
||||
result += m_invisibleDeclarations.at(_name);
|
||||
if (result.empty() && _recursive && m_enclosingContainer)
|
||||
result = m_enclosingContainer->resolveName(_name, true, _alsoInvisible);
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const
|
||||
@ -129,6 +142,12 @@ vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) con
|
||||
if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
|
||||
similar.push_back(declarationName);
|
||||
}
|
||||
for (auto const& declaration: m_invisibleDeclarations)
|
||||
{
|
||||
string const& declarationName = declaration.first;
|
||||
if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
|
||||
similar.push_back(declarationName);
|
||||
}
|
||||
|
||||
if (m_enclosingContainer)
|
||||
similar += m_enclosingContainer->similarNames(_name);
|
||||
|
@ -51,13 +51,17 @@ public:
|
||||
/// @param _update if true, replaces a potential declaration that is already present
|
||||
/// @returns false if the name was already declared.
|
||||
bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false);
|
||||
std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const;
|
||||
std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false, bool _alsoInvisible = false) const;
|
||||
ASTNode const* enclosingNode() const { return m_enclosingNode; }
|
||||
DeclarationContainer const* enclosingContainer() const { return m_enclosingContainer; }
|
||||
std::map<ASTString, std::vector<Declaration const*>> const& declarations() const { return m_declarations; }
|
||||
/// @returns whether declaration is valid, and if not also returns previous declaration.
|
||||
Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const;
|
||||
|
||||
/// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for
|
||||
/// VariableDeclarationStatements.
|
||||
void activateVariable(ASTString const& _name);
|
||||
|
||||
/// @returns existing declaration names similar to @a _name.
|
||||
/// Searches this and all parent containers.
|
||||
std::vector<ASTString> similarNames(ASTString const& _name) const;
|
||||
|
@ -50,12 +50,13 @@ NameAndTypeResolver::NameAndTypeResolver(
|
||||
m_scopes[nullptr]->registerDeclaration(*declaration);
|
||||
}
|
||||
|
||||
bool NameAndTypeResolver::registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope)
|
||||
bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope)
|
||||
{
|
||||
bool useC99Scoping = _sourceUnit.annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||
// The helper registers all declarations in m_scopes as a side-effect of its construction.
|
||||
try
|
||||
{
|
||||
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errorReporter, _currentScope);
|
||||
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, useC99Scoping, m_errorReporter, _currentScope);
|
||||
}
|
||||
catch (FatalError const&)
|
||||
{
|
||||
@ -106,7 +107,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
||||
else
|
||||
for (Declaration const* declaration: declarations)
|
||||
if (!DeclarationRegistrationHelper::registerDeclaration(
|
||||
target, *declaration, alias.second.get(), &imp->location(), true, m_errorReporter
|
||||
target, *declaration, alias.second.get(), &imp->location(), true, false, m_errorReporter
|
||||
))
|
||||
error = true;
|
||||
}
|
||||
@ -114,7 +115,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
||||
for (auto const& nameAndDeclaration: scope->second->declarations())
|
||||
for (auto const& declaration: nameAndDeclaration.second)
|
||||
if (!DeclarationRegistrationHelper::registerDeclaration(
|
||||
target, *declaration, &nameAndDeclaration.first, &imp->location(), true, m_errorReporter
|
||||
target, *declaration, &nameAndDeclaration.first, &imp->location(), true, false, m_errorReporter
|
||||
))
|
||||
error = true;
|
||||
}
|
||||
@ -151,6 +152,12 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
|
||||
return true;
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::activateVariable(string const& _name)
|
||||
{
|
||||
solAssert(m_currentScope, "");
|
||||
m_currentScope->activateVariable(_name);
|
||||
}
|
||||
|
||||
vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, ASTNode const* _scope) const
|
||||
{
|
||||
auto iterator = m_scopes.find(_scope);
|
||||
@ -159,15 +166,15 @@ vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _na
|
||||
return iterator->second->resolveName(_name, false);
|
||||
}
|
||||
|
||||
vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _recursive) const
|
||||
vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _includeInvisibles) const
|
||||
{
|
||||
return m_currentScope->resolveName(_name, _recursive);
|
||||
return m_currentScope->resolveName(_name, true, _includeInvisibles);
|
||||
}
|
||||
|
||||
Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> const& _path, bool _recursive) const
|
||||
Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> const& _path) const
|
||||
{
|
||||
solAssert(!_path.empty(), "");
|
||||
vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), _recursive);
|
||||
vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), true);
|
||||
for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++)
|
||||
{
|
||||
if (!m_scopes.count(candidates.front()))
|
||||
@ -229,7 +236,7 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
|
||||
for (auto const& instruction: c_instructions)
|
||||
{
|
||||
string const instructionName{boost::algorithm::to_lower_copy(instruction.first)};
|
||||
auto declarations = nameFromCurrentScope(instructionName);
|
||||
auto declarations = nameFromCurrentScope(instructionName, true);
|
||||
for (Declaration const* const declaration: declarations)
|
||||
{
|
||||
solAssert(!!declaration, "");
|
||||
@ -244,19 +251,24 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
|
||||
}
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::setScope(ASTNode const* _node)
|
||||
{
|
||||
m_currentScope = m_scopes[_node].get();
|
||||
}
|
||||
|
||||
bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode)
|
||||
{
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
|
||||
{
|
||||
bool success = true;
|
||||
m_currentScope = m_scopes[contract->scope()].get();
|
||||
setScope(contract->scope());
|
||||
solAssert(!!m_currentScope, "");
|
||||
|
||||
for (ASTPointer<InheritanceSpecifier> const& baseContract: contract->baseContracts())
|
||||
if (!resolveNamesAndTypes(*baseContract, true))
|
||||
success = false;
|
||||
|
||||
m_currentScope = m_scopes[contract].get();
|
||||
setScope(contract);
|
||||
|
||||
if (success)
|
||||
{
|
||||
@ -273,7 +285,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
|
||||
// these can contain code, only resolve parameters for now
|
||||
for (ASTPointer<ASTNode> const& node: contract->subNodes())
|
||||
{
|
||||
m_currentScope = m_scopes[contract].get();
|
||||
setScope(contract);
|
||||
if (!resolveNamesAndTypes(*node, false))
|
||||
{
|
||||
success = false;
|
||||
@ -287,12 +299,12 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
|
||||
if (!_resolveInsideCode)
|
||||
return success;
|
||||
|
||||
m_currentScope = m_scopes[contract].get();
|
||||
setScope(contract);
|
||||
|
||||
// now resolve references inside the code
|
||||
for (ASTPointer<ASTNode> const& node: contract->subNodes())
|
||||
{
|
||||
m_currentScope = m_scopes[contract].get();
|
||||
setScope(contract);
|
||||
if (!resolveNamesAndTypes(*node, true))
|
||||
success = false;
|
||||
}
|
||||
@ -301,7 +313,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
|
||||
else
|
||||
{
|
||||
if (m_scopes.count(&_node))
|
||||
m_currentScope = m_scopes[&_node].get();
|
||||
setScope(&_node);
|
||||
return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node);
|
||||
}
|
||||
}
|
||||
@ -434,9 +446,11 @@ string NameAndTypeResolver::similarNameSuggestions(ASTString const& _name) const
|
||||
DeclarationRegistrationHelper::DeclarationRegistrationHelper(
|
||||
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
|
||||
ASTNode& _astRoot,
|
||||
bool _useC99Scoping,
|
||||
ErrorReporter& _errorReporter,
|
||||
ASTNode const* _currentScope
|
||||
):
|
||||
m_useC99Scoping(_useC99Scoping),
|
||||
m_scopes(_scopes),
|
||||
m_currentScope(_currentScope),
|
||||
m_errorReporter(_errorReporter)
|
||||
@ -451,6 +465,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
||||
string const* _name,
|
||||
SourceLocation const* _errorLocation,
|
||||
bool _warnOnShadow,
|
||||
bool _inactive,
|
||||
ErrorReporter& _errorReporter
|
||||
)
|
||||
{
|
||||
@ -460,10 +475,13 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
||||
string name = _name ? *_name : _declaration.name();
|
||||
Declaration const* shadowedDeclaration = nullptr;
|
||||
if (_warnOnShadow && !name.empty() && _container.enclosingContainer())
|
||||
for (auto const* decl: _container.enclosingContainer()->resolveName(name, true))
|
||||
for (auto const* decl: _container.enclosingContainer()->resolveName(name, true, true))
|
||||
shadowedDeclaration = decl;
|
||||
|
||||
if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract()))
|
||||
// We use "invisible" for both inactive variables in blocks and for members invisible in contracts.
|
||||
// They cannot both be true at the same time.
|
||||
solAssert(!(_inactive && !_declaration.isVisibleInContract()), "");
|
||||
if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract() || _inactive))
|
||||
{
|
||||
SourceLocation firstDeclarationLocation;
|
||||
SourceLocation secondDeclarationLocation;
|
||||
@ -605,6 +623,34 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
|
||||
closeCurrentScope();
|
||||
}
|
||||
|
||||
bool DeclarationRegistrationHelper::visit(Block& _block)
|
||||
{
|
||||
_block.setScope(m_currentScope);
|
||||
if (m_useC99Scoping)
|
||||
enterNewSubScope(_block);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeclarationRegistrationHelper::endVisit(Block&)
|
||||
{
|
||||
if (m_useC99Scoping)
|
||||
closeCurrentScope();
|
||||
}
|
||||
|
||||
bool DeclarationRegistrationHelper::visit(ForStatement& _for)
|
||||
{
|
||||
_for.setScope(m_currentScope);
|
||||
if (m_useC99Scoping)
|
||||
enterNewSubScope(_for);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeclarationRegistrationHelper::endVisit(ForStatement&)
|
||||
{
|
||||
if (m_useC99Scoping)
|
||||
closeCurrentScope();
|
||||
}
|
||||
|
||||
void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement)
|
||||
{
|
||||
// Register the local variables with the function
|
||||
@ -632,14 +678,14 @@ void DeclarationRegistrationHelper::endVisit(EventDefinition&)
|
||||
closeCurrentScope();
|
||||
}
|
||||
|
||||
void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
|
||||
void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope)
|
||||
{
|
||||
map<ASTNode const*, shared_ptr<DeclarationContainer>>::iterator iter;
|
||||
bool newlyAdded;
|
||||
shared_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get()));
|
||||
tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, move(container));
|
||||
tie(iter, newlyAdded) = m_scopes.emplace(&_subScope, move(container));
|
||||
solAssert(newlyAdded, "Unable to add new scope.");
|
||||
m_currentScope = &_declaration;
|
||||
m_currentScope = &_subScope;
|
||||
}
|
||||
|
||||
void DeclarationRegistrationHelper::closeCurrentScope()
|
||||
@ -667,7 +713,12 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
|
||||
if (fun->isConstructor())
|
||||
warnAboutShadowing = false;
|
||||
|
||||
registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, m_errorReporter);
|
||||
// Register declaration as inactive if we are in block scope and C99 mode.
|
||||
bool inactive =
|
||||
m_useC99Scoping &&
|
||||
(dynamic_cast<Block const*>(m_currentScope) || dynamic_cast<ForStatement const*>(m_currentScope));
|
||||
|
||||
registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, inactive, m_errorReporter);
|
||||
|
||||
_declaration.setScope(m_currentScope);
|
||||
if (_opensScope)
|
||||
|
@ -56,7 +56,7 @@ public:
|
||||
/// @returns false in case of error.
|
||||
/// @param _currentScope should be nullptr but can be used to inject new declarations into
|
||||
/// existing scopes, used by the snippets feature.
|
||||
bool registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope = nullptr);
|
||||
bool registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope = nullptr);
|
||||
/// Applies the effect of import directives.
|
||||
bool performImports(SourceUnit& _sourceUnit, std::map<std::string, SourceUnit const*> const& _sourceUnits);
|
||||
/// Resolves all names and types referenced from the given AST Node.
|
||||
@ -69,20 +69,24 @@ public:
|
||||
/// that create their own scope.
|
||||
/// @returns false in case of error.
|
||||
bool updateDeclaration(Declaration const& _declaration);
|
||||
/// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for
|
||||
/// VariableDeclarationStatements.
|
||||
void activateVariable(std::string const& _name);
|
||||
|
||||
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
|
||||
/// the global scope is used (i.e. the one containing only the pre-defined global variables).
|
||||
/// @returns a pointer to the declaration on success or nullptr on failure.
|
||||
/// SHOULD only be used for testing.
|
||||
std::vector<Declaration const*> resolveName(ASTString const& _name, ASTNode const* _scope = nullptr) const;
|
||||
|
||||
/// Resolves a name in the "current" scope. Should only be called during the initial
|
||||
/// resolving phase.
|
||||
std::vector<Declaration const*> nameFromCurrentScope(ASTString const& _name, bool _recursive = true) const;
|
||||
/// Resolves a name in the "current" scope, but also searches parent scopes.
|
||||
/// Should only be called during the initial resolving phase.
|
||||
std::vector<Declaration const*> nameFromCurrentScope(ASTString const& _name, bool _includeInvisibles = false) const;
|
||||
|
||||
/// Resolves a path starting from the "current" scope. Should only be called during the initial
|
||||
/// resolving phase.
|
||||
/// Resolves a path starting from the "current" scope, but also searches parent scopes.
|
||||
/// Should only be called during the initial resolving phase.
|
||||
/// @note Returns a null pointer if any component in the path was not unique or not found.
|
||||
Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path, bool _recursive = true) const;
|
||||
Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path) const;
|
||||
|
||||
/// returns the vector of declarations without repetitions
|
||||
std::vector<Declaration const*> cleanedDeclarations(
|
||||
@ -96,6 +100,9 @@ public:
|
||||
/// @returns a list of similar identifiers in the current and enclosing scopes. May return empty string if no suggestions.
|
||||
std::string similarNameSuggestions(ASTString const& _name) const;
|
||||
|
||||
/// Sets the current scope.
|
||||
void setScope(ASTNode const* _node);
|
||||
|
||||
private:
|
||||
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
|
||||
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
|
||||
@ -135,6 +142,7 @@ public:
|
||||
DeclarationRegistrationHelper(
|
||||
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
|
||||
ASTNode& _astRoot,
|
||||
bool _useC99Scoping,
|
||||
ErrorReporter& _errorReporter,
|
||||
ASTNode const* _currentScope = nullptr
|
||||
);
|
||||
@ -145,6 +153,7 @@ public:
|
||||
std::string const* _name,
|
||||
SourceLocation const* _errorLocation,
|
||||
bool _warnOnShadow,
|
||||
bool _inactive,
|
||||
ErrorReporter& _errorReporter
|
||||
);
|
||||
|
||||
@ -163,12 +172,16 @@ private:
|
||||
void endVisit(FunctionDefinition& _function) override;
|
||||
bool visit(ModifierDefinition& _modifier) override;
|
||||
void endVisit(ModifierDefinition& _modifier) override;
|
||||
bool visit(Block& _block) override;
|
||||
void endVisit(Block& _block) override;
|
||||
bool visit(ForStatement& _forLoop) override;
|
||||
void endVisit(ForStatement& _forLoop) override;
|
||||
void endVisit(VariableDeclarationStatement& _variableDeclarationStatement) override;
|
||||
bool visit(VariableDeclaration& _declaration) override;
|
||||
bool visit(EventDefinition& _event) override;
|
||||
void endVisit(EventDefinition& _event) override;
|
||||
|
||||
void enterNewSubScope(Declaration const& _declaration);
|
||||
void enterNewSubScope(ASTNode& _subScope);
|
||||
void closeCurrentScope();
|
||||
void registerDeclaration(Declaration& _declaration, bool _opensScope);
|
||||
|
||||
@ -177,6 +190,7 @@ private:
|
||||
/// @returns the canonical name of the current scope.
|
||||
std::string currentCanonicalName() const;
|
||||
|
||||
bool m_useC99Scoping = false;
|
||||
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
|
||||
ASTNode const* m_currentScope = nullptr;
|
||||
VariableScope* m_currentFunction = nullptr;
|
||||
|
@ -43,6 +43,56 @@ bool ReferencesResolver::resolve(ASTNode const& _root)
|
||||
return !m_errorOccurred;
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(Block const& _block)
|
||||
{
|
||||
if (!m_resolveInsideCode)
|
||||
return false;
|
||||
m_experimental050Mode = _block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||
// C99-scoped variables
|
||||
if (m_experimental050Mode)
|
||||
m_resolver.setScope(&_block);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReferencesResolver::endVisit(Block const& _block)
|
||||
{
|
||||
if (!m_resolveInsideCode)
|
||||
return;
|
||||
|
||||
// C99-scoped variables
|
||||
if (m_experimental050Mode)
|
||||
m_resolver.setScope(_block.scope());
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(ForStatement const& _for)
|
||||
{
|
||||
if (!m_resolveInsideCode)
|
||||
return false;
|
||||
m_experimental050Mode = _for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||
// C99-scoped variables
|
||||
if (m_experimental050Mode)
|
||||
m_resolver.setScope(&_for);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReferencesResolver::endVisit(ForStatement const& _for)
|
||||
{
|
||||
if (!m_resolveInsideCode)
|
||||
return;
|
||||
if (m_experimental050Mode)
|
||||
m_resolver.setScope(_for.scope());
|
||||
}
|
||||
|
||||
void ReferencesResolver::endVisit(VariableDeclarationStatement const& _varDeclStatement)
|
||||
{
|
||||
if (!m_resolveInsideCode)
|
||||
return;
|
||||
if (m_experimental050Mode)
|
||||
for (auto const& var: _varDeclStatement.declarations())
|
||||
if (var)
|
||||
m_resolver.activateVariable(var->name());
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(Identifier const& _identifier)
|
||||
{
|
||||
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
|
||||
|
@ -57,7 +57,11 @@ public:
|
||||
bool resolve(ASTNode const& _root);
|
||||
|
||||
private:
|
||||
virtual bool visit(Block const&) override { return m_resolveInsideCode; }
|
||||
virtual bool visit(Block const& _block) override;
|
||||
virtual void endVisit(Block const& _block) override;
|
||||
virtual bool visit(ForStatement const& _for) override;
|
||||
virtual void endVisit(ForStatement const& _for) override;
|
||||
virtual void endVisit(VariableDeclarationStatement const& _varDeclStatement) override;
|
||||
virtual bool visit(Identifier const& _identifier) override;
|
||||
virtual bool visit(ElementaryTypeName const& _typeName) override;
|
||||
virtual bool visit(FunctionDefinition const& _functionDefinition) override;
|
||||
@ -90,6 +94,7 @@ private:
|
||||
std::vector<ParameterList const*> m_returnParameters;
|
||||
bool const m_resolveInsideCode;
|
||||
bool m_errorOccurred = false;
|
||||
bool m_experimental050Mode = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -96,20 +96,6 @@ set<SourceUnit const*> SourceUnit::referencedSourceUnits(bool _recurse, set<Sour
|
||||
return sourceUnits;
|
||||
}
|
||||
|
||||
SourceUnit const& Declaration::sourceUnit() const
|
||||
{
|
||||
solAssert(!!m_scope, "");
|
||||
ASTNode const* scope = m_scope;
|
||||
while (dynamic_cast<Declaration const*>(scope) && dynamic_cast<Declaration const*>(scope)->m_scope)
|
||||
scope = dynamic_cast<Declaration const*>(scope)->m_scope;
|
||||
return dynamic_cast<SourceUnit const&>(*scope);
|
||||
}
|
||||
|
||||
string Declaration::sourceUnitName() const
|
||||
{
|
||||
return sourceUnit().annotation().path;
|
||||
}
|
||||
|
||||
ImportAnnotation& ImportDirective::annotation() const
|
||||
{
|
||||
if (!m_annotation)
|
||||
@ -408,12 +394,36 @@ UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const
|
||||
return dynamic_cast<UserDefinedTypeNameAnnotation&>(*m_annotation);
|
||||
}
|
||||
|
||||
SourceUnit const& Scopable::sourceUnit() const
|
||||
{
|
||||
ASTNode const* s = scope();
|
||||
solAssert(s, "");
|
||||
// will not always be a declaratoion
|
||||
while (dynamic_cast<Scopable const*>(s) && dynamic_cast<Scopable const*>(s)->scope())
|
||||
s = dynamic_cast<Scopable const*>(s)->scope();
|
||||
return dynamic_cast<SourceUnit const&>(*s);
|
||||
}
|
||||
|
||||
string Scopable::sourceUnitName() const
|
||||
{
|
||||
return sourceUnit().annotation().path;
|
||||
}
|
||||
|
||||
bool VariableDeclaration::isLValue() const
|
||||
{
|
||||
// External function parameters and constant declared variables are Read-Only
|
||||
return !isExternalCallableParameter() && !m_isConstant;
|
||||
}
|
||||
|
||||
bool VariableDeclaration::isLocalVariable() const
|
||||
{
|
||||
auto s = scope();
|
||||
return
|
||||
dynamic_cast<CallableDeclaration const*>(s) ||
|
||||
dynamic_cast<Block const*>(s) ||
|
||||
dynamic_cast<ForStatement const*>(s);
|
||||
}
|
||||
|
||||
bool VariableDeclaration::isCallableParameter() const
|
||||
{
|
||||
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
|
||||
@ -459,8 +469,7 @@ bool VariableDeclaration::isExternalCallableParameter() const
|
||||
|
||||
bool VariableDeclaration::canHaveAutoType() const
|
||||
{
|
||||
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
|
||||
return (!!callable && !isCallableParameter());
|
||||
return isLocalVariable() && !isCallableParameter();
|
||||
}
|
||||
|
||||
TypePointer VariableDeclaration::type() const
|
||||
|
@ -139,10 +139,33 @@ private:
|
||||
std::vector<ASTPointer<ASTNode>> m_nodes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract class that is added to each AST node that is stored inside a scope
|
||||
* (including scopes).
|
||||
*/
|
||||
class Scopable
|
||||
{
|
||||
public:
|
||||
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
|
||||
/// Available only after name and type resolution step.
|
||||
ASTNode const* scope() const { return m_scope; }
|
||||
void setScope(ASTNode const* _scope) { m_scope = _scope; }
|
||||
|
||||
/// @returns the source unit this scopable is present in.
|
||||
SourceUnit const& sourceUnit() const;
|
||||
|
||||
/// @returns the source name this scopable is present in.
|
||||
/// Can be combined with annotation().canonicalName (if present) to form a globally unique name.
|
||||
std::string sourceUnitName() const;
|
||||
|
||||
protected:
|
||||
ASTNode const* m_scope = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract AST class for a declaration (contract, function, struct, variable, import directive).
|
||||
*/
|
||||
class Declaration: public ASTNode
|
||||
class Declaration: public ASTNode, public Scopable
|
||||
{
|
||||
public:
|
||||
/// Visibility ordered from restricted to unrestricted.
|
||||
@ -171,7 +194,7 @@ public:
|
||||
ASTPointer<ASTString> const& _name,
|
||||
Visibility _visibility = Visibility::Default
|
||||
):
|
||||
ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {}
|
||||
ASTNode(_location), m_name(_name), m_visibility(_visibility) {}
|
||||
|
||||
/// @returns the declared name.
|
||||
ASTString const& name() const { return *m_name; }
|
||||
@ -181,17 +204,6 @@ public:
|
||||
virtual bool isVisibleInContract() const { return visibility() != Visibility::External; }
|
||||
bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; }
|
||||
|
||||
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
|
||||
/// Available only after name and type resolution step.
|
||||
ASTNode const* scope() const { return m_scope; }
|
||||
void setScope(ASTNode const* _scope) { m_scope = _scope; }
|
||||
|
||||
/// @returns the source unit this declaration is present in.
|
||||
SourceUnit const& sourceUnit() const;
|
||||
|
||||
/// @returns the source name this declaration is present in.
|
||||
/// Can be combined with annotation().canonicalName to form a globally unique name.
|
||||
std::string sourceUnitName() const;
|
||||
std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); }
|
||||
|
||||
virtual bool isLValue() const { return false; }
|
||||
@ -213,7 +225,6 @@ protected:
|
||||
private:
|
||||
ASTPointer<ASTString> m_name;
|
||||
Visibility m_visibility;
|
||||
ASTNode const* m_scope;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -289,6 +300,8 @@ private:
|
||||
|
||||
/**
|
||||
* Abstract class that is added to each AST node that can store local variables.
|
||||
* Local variables in functions are always added to functions, even though they are not
|
||||
* in scope for the whole function.
|
||||
*/
|
||||
class VariableScope
|
||||
{
|
||||
@ -662,7 +675,7 @@ public:
|
||||
virtual bool isLValue() const override;
|
||||
virtual bool isPartOfExternalInterface() const override { return isPublic(); }
|
||||
|
||||
bool isLocalVariable() const { return !!dynamic_cast<CallableDeclaration const*>(scope()); }
|
||||
bool isLocalVariable() const;
|
||||
/// @returns true if this variable is a parameter or return parameter of a function.
|
||||
bool isCallableParameter() const;
|
||||
/// @returns true if this variable is a return parameter of a function.
|
||||
@ -1004,7 +1017,7 @@ private:
|
||||
/**
|
||||
* Brace-enclosed block containing zero or more statements.
|
||||
*/
|
||||
class Block: public Statement
|
||||
class Block: public Statement, public Scopable
|
||||
{
|
||||
public:
|
||||
Block(
|
||||
@ -1111,7 +1124,7 @@ private:
|
||||
/**
|
||||
* For loop statement
|
||||
*/
|
||||
class ForStatement: public BreakableStatement
|
||||
class ForStatement: public BreakableStatement, public Scopable
|
||||
{
|
||||
public:
|
||||
ForStatement(
|
||||
|
@ -466,7 +466,8 @@ BOOST_AUTO_TEST_CASE(for_loop)
|
||||
text = R"(
|
||||
contract C {
|
||||
function f(uint x) public pure {
|
||||
for (uint y = 2; x < 10; ) {
|
||||
uint y;
|
||||
for (y = 2; x < 10; ) {
|
||||
y = 3;
|
||||
}
|
||||
assert(y == 3);
|
||||
@ -477,7 +478,8 @@ BOOST_AUTO_TEST_CASE(for_loop)
|
||||
text = R"(
|
||||
contract C {
|
||||
function f(uint x) public pure {
|
||||
for (uint y = 2; x < 10; ) {
|
||||
uint y;
|
||||
for (y = 2; x < 10; ) {
|
||||
y = 3;
|
||||
}
|
||||
assert(y == 2);
|
||||
|
@ -284,6 +284,54 @@ BOOST_AUTO_TEST_CASE(conditional_expression_functions)
|
||||
ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(u256(2)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(C99_scoping_activation)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
pragma experimental "v0.5.0";
|
||||
contract test {
|
||||
function f() pure public returns (uint) {
|
||||
uint x = 7;
|
||||
{
|
||||
x = 3; // This should still assign to the outer variable
|
||||
uint x;
|
||||
x = 4; // This should assign to the new one
|
||||
}
|
||||
return x;
|
||||
}
|
||||
function g() pure public returns (uint x) {
|
||||
x = 7;
|
||||
{
|
||||
x = 3;
|
||||
uint x;
|
||||
return x; // This returns the new variable, i.e. 0
|
||||
}
|
||||
}
|
||||
function h() pure public returns (uint x, uint a, uint b) {
|
||||
x = 7;
|
||||
{
|
||||
x = 3;
|
||||
a = x; // This should read from the outer
|
||||
uint x = 4;
|
||||
b = x;
|
||||
}
|
||||
}
|
||||
function i() pure public returns (uint x, uint a) {
|
||||
x = 7;
|
||||
{
|
||||
x = 3;
|
||||
uint x = x; // This should read from the outer and assign to the inner
|
||||
a = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("f()"), encodeArgs(3));
|
||||
ABI_CHECK(callContractFunction("g()"), encodeArgs(0));
|
||||
ABI_CHECK(callContractFunction("h()"), encodeArgs(3, 3, 4));
|
||||
ABI_CHECK(callContractFunction("i()"), encodeArgs(3, 3));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(recursive_calls)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
|
@ -322,10 +322,10 @@ BOOST_AUTO_TEST_CASE(arithmetics)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f(uint y) { var x = ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); }
|
||||
function f(uint y) { ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); }
|
||||
}
|
||||
)";
|
||||
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}});
|
||||
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
|
||||
bytes expectation({byte(Instruction::PUSH1), 0x1,
|
||||
byte(Instruction::PUSH1), 0x2,
|
||||
byte(Instruction::PUSH1), 0x3,
|
||||
@ -334,7 +334,7 @@ BOOST_AUTO_TEST_CASE(arithmetics)
|
||||
byte(Instruction::PUSH1), 0x6,
|
||||
byte(Instruction::PUSH1), 0x7,
|
||||
byte(Instruction::PUSH1), 0x8,
|
||||
byte(Instruction::DUP10),
|
||||
byte(Instruction::DUP9),
|
||||
byte(Instruction::XOR),
|
||||
byte(Instruction::AND),
|
||||
byte(Instruction::OR),
|
||||
@ -364,13 +364,13 @@ BOOST_AUTO_TEST_CASE(unary_operators)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f(int y) { var x = !(~+- y == 2); }
|
||||
function f(int y) { !(~+- y == 2); }
|
||||
}
|
||||
)";
|
||||
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}});
|
||||
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
|
||||
|
||||
bytes expectation({byte(Instruction::PUSH1), 0x2,
|
||||
byte(Instruction::DUP3),
|
||||
byte(Instruction::DUP2),
|
||||
byte(Instruction::PUSH1), 0x0,
|
||||
byte(Instruction::SUB),
|
||||
byte(Instruction::NOT),
|
||||
@ -383,7 +383,7 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f(uint a) { var x = --a ^ (a-- ^ (++a ^ a++)); }
|
||||
function f(uint a) returns (uint x) { x = --a ^ (a-- ^ (++a ^ a++)); }
|
||||
}
|
||||
)";
|
||||
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}});
|
||||
@ -426,7 +426,10 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec)
|
||||
byte(Instruction::POP), // second ++
|
||||
// Stack here: a x a^(a+2)^(a+2)
|
||||
byte(Instruction::DUP3), // will change
|
||||
byte(Instruction::XOR)});
|
||||
byte(Instruction::XOR),
|
||||
byte(Instruction::SWAP1),
|
||||
byte(Instruction::POP),
|
||||
byte(Instruction::DUP1)});
|
||||
// Stack here: a x a^(a+2)^(a+2)^a
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||
}
|
||||
|
@ -76,15 +76,236 @@ BOOST_AUTO_TEST_CASE(double_function_declaration)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(double_variable_declaration)
|
||||
{
|
||||
char const* text = R"(
|
||||
string text = R"(
|
||||
contract test {
|
||||
function f() public {
|
||||
function f() pure public {
|
||||
uint256 x;
|
||||
if (true) { uint256 x; }
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, DeclarationError, "Identifier already declared.");
|
||||
CHECK_ERROR(text, DeclarationError, "Identifier already declared");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(double_variable_declaration_050)
|
||||
{
|
||||
string text = R"(
|
||||
pragma experimental "v0.5.0";
|
||||
contract test {
|
||||
function f() pure public {
|
||||
uint256 x;
|
||||
if (true) { uint256 x; }
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
|
||||
"This declaration shadows an existing declaration.",
|
||||
"Experimental features",
|
||||
"Unused local variable",
|
||||
"Unused local variable"
|
||||
}));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope)
|
||||
{
|
||||
string text = R"(
|
||||
contract test {
|
||||
function f() pure public {
|
||||
{ uint x; }
|
||||
{ uint x; }
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, DeclarationError, "Identifier already declared");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_050)
|
||||
{
|
||||
string text = R"(
|
||||
pragma experimental "v0.5.0";
|
||||
contract test {
|
||||
function f() pure public {
|
||||
{ uint x; }
|
||||
{ uint x; }
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
|
||||
"Experimental features",
|
||||
"Unused local variable",
|
||||
"Unused local variable"
|
||||
}));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation)
|
||||
{
|
||||
string text = R"(
|
||||
contract test {
|
||||
function f() pure public {
|
||||
{ uint x; }
|
||||
uint x;
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, DeclarationError, "Identifier already declared");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation_050)
|
||||
{
|
||||
string text = R"(
|
||||
pragma experimental "v0.5.0";
|
||||
contract test {
|
||||
function f() pure public {
|
||||
{ uint x; }
|
||||
uint x;
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
|
||||
"Experimental features",
|
||||
"Unused local variable",
|
||||
"Unused local variable"
|
||||
}));
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE(scoping_old)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract test {
|
||||
function f() pure public {
|
||||
x = 4;
|
||||
uint256 x = 2;
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_SUCCESS_NO_WARNINGS(text);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scoping)
|
||||
{
|
||||
char const* text = R"(
|
||||
pragma experimental "v0.5.0";
|
||||
contract test {
|
||||
function f() public {
|
||||
{
|
||||
uint256 x;
|
||||
}
|
||||
x = 2;
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scoping_activation_old)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract test {
|
||||
function f() pure public {
|
||||
x = 3;
|
||||
uint x;
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_SUCCESS_NO_WARNINGS(text);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scoping_activation)
|
||||
{
|
||||
char const* text = R"(
|
||||
pragma experimental "v0.5.0";
|
||||
contract test {
|
||||
function f() pure public {
|
||||
x = 3;
|
||||
uint x;
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scoping_self_use)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract test {
|
||||
function f() pure public {
|
||||
uint a = a;
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_SUCCESS_NO_WARNINGS(text);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scoping_self_use_050)
|
||||
{
|
||||
char const* text = R"(
|
||||
pragma experimental "v0.5.0";
|
||||
contract test {
|
||||
function f() pure public {
|
||||
uint a = a;
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scoping_for)
|
||||
{
|
||||
char const* text = R"(
|
||||
pragma experimental "v0.5.0";
|
||||
contract test {
|
||||
function f() pure public {
|
||||
for (uint x = 0; x < 10; x ++){
|
||||
x = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_WARNING(text, "Experimental features");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scoping_for2)
|
||||
{
|
||||
char const* text = R"(
|
||||
pragma experimental "v0.5.0";
|
||||
contract test {
|
||||
function f() pure public {
|
||||
for (uint x = 0; x < 10; x ++)
|
||||
x = 2;
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_WARNING(text, "Experimental features");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scoping_for3)
|
||||
{
|
||||
char const* text = R"(
|
||||
pragma experimental "v0.5.0";
|
||||
contract test {
|
||||
function f() pure public {
|
||||
for (uint x = 0; x < 10; x ++){
|
||||
x = 2;
|
||||
}
|
||||
x = 4;
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scoping_for_decl_in_body)
|
||||
{
|
||||
char const* text = R"(
|
||||
pragma experimental "v0.5.0";
|
||||
contract test {
|
||||
function f() pure public {
|
||||
for (;; y++){
|
||||
uint y = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(name_shadowing)
|
||||
@ -1004,7 +1225,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract B {
|
||||
function f() mod1(2, true) mod2("0123456") public { }
|
||||
function f() mod1(2, true) mod2("0123456") pure public { }
|
||||
modifier mod1(uint a, bool b) { if (b) _; }
|
||||
modifier mod2(bytes7 a) { while (a == "1234567") _; }
|
||||
}
|
||||
@ -1039,11 +1260,23 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract B {
|
||||
function f() mod(x) public { uint x = 7; }
|
||||
function f() mod(x) pure public { uint x = 7; }
|
||||
modifier mod(uint a) { if (a > 0) _; }
|
||||
}
|
||||
)";
|
||||
CHECK_SUCCESS(text);
|
||||
CHECK_SUCCESS_NO_WARNINGS(text);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables050)
|
||||
{
|
||||
char const* text = R"(
|
||||
pragma experimental "v0.5.0";
|
||||
contract B {
|
||||
function f() mod(x) pure public { uint x = 7; }
|
||||
modifier mod(uint a) { if (a > 0) _; }
|
||||
}
|
||||
)";
|
||||
CHECK_ERROR(text, DeclarationError, "Undeclared identifier.");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(function_modifier_double_invocation)
|
||||
|
Loading…
Reference in New Issue
Block a user