Fix shadowing struct types by struct member names

This commit is contained in:
a3d4 2021-02-08 05:50:58 +01:00 committed by chriseth
parent 70b8b1c834
commit f59145f21f
11 changed files with 133 additions and 15 deletions

View File

@ -21,6 +21,7 @@ Bugfixes:
* Control Flow Graph: Take internal calls to functions that always revert into account for reporting unused or unassigned variables.
* Control Flow Graph: Assume unimplemented modifiers use a placeholder.
* Function Call Graph: Fix internal error connected with circular constant references.
* Name Resolver: Do not issue shadowing warning if the shadowing name is not directly accessible.
* Natspec: Allow multiple ``@return`` tags on public state variable documentation.
* SMTChecker: Fix internal error on struct constructor with fixed bytes member initialized with string literal.
* SMTChecker: Fix internal error on external calls from the constructor.

View File

@ -26,6 +26,9 @@
#include <libsolidity/ast/AST.h>
#include <libsolutil/StringUtils.h>
#include <range/v3/view/filter.hpp>
#include <range/v3/range/conversion.hpp>
using namespace std;
using namespace solidity;
using namespace solidity::frontend;
@ -139,16 +142,35 @@ bool DeclarationContainer::registerDeclaration(
return registerDeclaration(_declaration, nullptr, nullptr, _invisible, _update);
}
vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive, bool _alsoInvisible) const
vector<Declaration const*> DeclarationContainer::resolveName(
ASTString const& _name,
bool _recursive,
bool _alsoInvisible,
bool _onlyVisibleAsUnqualifiedNames
) const
{
solAssert(!_name.empty(), "Attempt to resolve empty name.");
vector<Declaration const*> result;
if (m_declarations.count(_name))
result = m_declarations.at(_name);
{
if (_onlyVisibleAsUnqualifiedNames)
result += m_declarations.at(_name) | ranges::views::filter(&Declaration::isVisibleAsUnqualifiedName) | ranges::to_vector;
else
result += m_declarations.at(_name);
}
if (_alsoInvisible && m_invisibleDeclarations.count(_name))
result += m_invisibleDeclarations.at(_name);
{
if (_onlyVisibleAsUnqualifiedNames)
result += m_invisibleDeclarations.at(_name) | ranges::views::filter(&Declaration::isVisibleAsUnqualifiedName) | ranges::to_vector;
else
result += m_invisibleDeclarations.at(_name);
}
if (result.empty() && _recursive && m_enclosingContainer)
result = m_enclosingContainer->resolveName(_name, true, _alsoInvisible);
result = m_enclosingContainer->resolveName(_name, true, _alsoInvisible, _onlyVisibleAsUnqualifiedNames);
return result;
}

View File

@ -39,11 +39,10 @@ class DeclarationContainer
public:
using Homonyms = std::vector<std::pair<langutil::SourceLocation const*, std::vector<Declaration const*>>>;
explicit DeclarationContainer(
ASTNode const* _enclosingNode = nullptr,
DeclarationContainer* _enclosingContainer = nullptr
):
m_enclosingNode(_enclosingNode), m_enclosingContainer(_enclosingContainer)
DeclarationContainer() = default;
explicit DeclarationContainer(ASTNode const* _enclosingNode, DeclarationContainer* _enclosingContainer):
m_enclosingNode(_enclosingNode),
m_enclosingContainer(_enclosingContainer)
{
if (_enclosingContainer)
_enclosingContainer->m_innerContainers.emplace_back(this);
@ -57,7 +56,20 @@ public:
bool registerDeclaration(Declaration const& _declaration, ASTString const* _name, langutil::SourceLocation const* _location, bool _invisible, bool _update);
bool registerDeclaration(Declaration const& _declaration, bool _invisible, bool _update);
std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false, bool _alsoInvisible = false) const;
/// Finds all declarations that in the current scope can be referred to using specified name.
/// @param _name the name to look for.
/// @param _recursive if true and there are no matching declarations in the current container,
/// recursively searches the enclosing containers as well.
/// @param _alsoInvisible if true, include invisible declaration in the results.
/// @param _onlyVisibleAsUnqualifiedNames if true, do not include declarations which can never
/// actually be referenced using their name alone (without being qualified with the name
/// of scope in which they are declared).
std::vector<Declaration const*> resolveName(
ASTString const& _name,
bool _recursive = false,
bool _alsoInvisible = false,
bool _onlyVisibleAsUnqualifiedNames = 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; }
@ -80,8 +92,8 @@ public:
void populateHomonyms(std::back_insert_iterator<Homonyms> _it) const;
private:
ASTNode const* m_enclosingNode;
DeclarationContainer const* m_enclosingContainer;
ASTNode const* m_enclosingNode = nullptr;
DeclarationContainer const* m_enclosingContainer = nullptr;
std::vector<DeclarationContainer const*> m_innerContainers;
std::map<ASTString, std::vector<Declaration const*>> m_declarations;
std::map<ASTString, std::vector<Declaration const*>> m_invisibleDeclarations;

View File

@ -182,7 +182,13 @@ vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString c
Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> const& _path) const
{
solAssert(!_path.empty(), "");
vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), true);
vector<Declaration const*> candidates = m_currentScope->resolveName(
_path.front(),
/* _recursive */ true,
/* _alsoInvisible */ false,
/* _onlyVisibleAsUnqualifiedNames */ true
);
for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++)
{
if (!m_scopes.count(candidates.front()))
@ -627,7 +633,10 @@ void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope)
solAssert(dynamic_cast<SourceUnit const*>(&_subScope), "Unexpected scope type.");
else
{
bool newlyAdded = m_scopes.emplace(&_subScope, make_shared<DeclarationContainer>(m_currentScope, m_scopes[m_currentScope].get())).second;
bool newlyAdded = m_scopes.emplace(
&_subScope,
make_shared<DeclarationContainer>(m_currentScope, m_scopes[m_currentScope].get())
).second;
solAssert(newlyAdded, "Unable to add new scope.");
}
m_currentScope = &_subScope;

View File

@ -0,0 +1,26 @@
pragma abicoder v2;
contract C {
enum EnumType {A, B, C}
struct StructType {
uint x;
}
error E1(StructType StructType);
error E2(EnumType StructType, StructType EnumType);
function f() public {
revert E1({StructType: StructType(42)});
}
function g() public {
revert E2({EnumType: StructType(42), StructType: EnumType.B});
}
}
// ====
// compileToEwasm: also
// compileViaYul: also
// ----
// f() -> FAILURE, hex"33a54193", hex"000000000000000000000000000000000000000000000000000000000000002a"
// g() -> FAILURE, hex"374b9387", hex"0000000000000000000000000000000000000000000000000000000000000001", hex"000000000000000000000000000000000000000000000000000000000000002a"

View File

@ -0,0 +1,12 @@
contract C {
struct S {
uint x;
}
enum E {E, S, C, a, f}
uint a;
function f() public pure {}
}
// ----

View File

@ -0,0 +1,5 @@
contract C {
error E(int bytes, bytes x);
}
// ----
// ParserError 2314: (29-34): Expected ',' but got 'bytes'

View File

@ -0,0 +1,12 @@
contract C {
enum EnumType {A, B, C}
struct StructType {
uint x;
}
error E1(StructType StructType);
error E2(EnumType EnumType);
error E3(EnumType StructType, StructType EnumType);
}
// ----

View File

@ -0,0 +1,13 @@
contract C {
enum EnumType {A, B, C}
struct StructType {
uint x;
}
event E1(StructType StructType);
event E2(EnumType EnumType);
event E3(EnumType StructType, StructType EnumType);
event E4(StructType indexed StructType) anonymous;
}
// ----

View File

@ -3,4 +3,4 @@ contract C {
function f(function(S memory) external) public {}
}
// ----
// TypeError 5172: (25-26): Name has to refer to a struct, enum or contract.
// DeclarationError 7920: (25-26): Identifier not found or not unique.

View File

@ -0,0 +1,6 @@
contract C {
enum E {a, b, c}
struct S {E X; uint E;}
struct T {E T; uint E;}
struct U {E E;}
}