mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #1637 from ethereum/warn-shadowing-globals
Warn if shadowing built-ins
This commit is contained in:
commit
925569bfa3
@ -8,6 +8,7 @@ Features:
|
|||||||
* Type Checker: Include types in explicit conversion error message.
|
* Type Checker: Include types in explicit conversion error message.
|
||||||
* Type Checker: Raise proper error for arrays too large for ABI encoding.
|
* Type Checker: Raise proper error for arrays too large for ABI encoding.
|
||||||
* Type checker: Warn if using ``this`` in a constructor.
|
* Type checker: Warn if using ``this`` in a constructor.
|
||||||
|
* Type checker: Warn when existing symbols, including builtins, are overwritten.
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
* Type Checker: Fix invalid "specify storage keyword" warning for reference members of structs.
|
* Type Checker: Fix invalid "specify storage keyword" warning for reference members of structs.
|
||||||
|
@ -104,29 +104,18 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
for (Declaration const* declaration: declarations)
|
for (Declaration const* declaration: declarations)
|
||||||
{
|
if (!DeclarationRegistrationHelper::registerDeclaration(
|
||||||
ASTString const* name = alias.second ? alias.second.get() : &declaration->name();
|
target, *declaration, alias.second.get(), &imp->location(), true, m_errorReporter
|
||||||
if (!target.registerDeclaration(*declaration, name))
|
))
|
||||||
{
|
|
||||||
m_errorReporter.declarationError(
|
|
||||||
imp->location(),
|
|
||||||
"Identifier \"" + *name + "\" already declared."
|
|
||||||
);
|
|
||||||
error = true;
|
error = true;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (imp->name().empty())
|
else if (imp->name().empty())
|
||||||
for (auto const& nameAndDeclaration: scope->second->declarations())
|
for (auto const& nameAndDeclaration: scope->second->declarations())
|
||||||
for (auto const& declaration: nameAndDeclaration.second)
|
for (auto const& declaration: nameAndDeclaration.second)
|
||||||
if (!target.registerDeclaration(*declaration, &nameAndDeclaration.first))
|
if (!DeclarationRegistrationHelper::registerDeclaration(
|
||||||
{
|
target, *declaration, &nameAndDeclaration.first, &imp->location(), true, m_errorReporter
|
||||||
m_errorReporter.declarationError(
|
))
|
||||||
imp->location(),
|
error = true;
|
||||||
"Identifier \"" + nameAndDeclaration.first + "\" already declared."
|
|
||||||
);
|
|
||||||
error = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return !error;
|
return !error;
|
||||||
}
|
}
|
||||||
@ -450,6 +439,75 @@ DeclarationRegistrationHelper::DeclarationRegistrationHelper(
|
|||||||
solAssert(m_currentScope == _currentScope, "Scopes not correctly closed.");
|
solAssert(m_currentScope == _currentScope, "Scopes not correctly closed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DeclarationRegistrationHelper::registerDeclaration(
|
||||||
|
DeclarationContainer& _container,
|
||||||
|
Declaration const& _declaration,
|
||||||
|
string const* _name,
|
||||||
|
SourceLocation const* _errorLocation,
|
||||||
|
bool _warnOnShadow,
|
||||||
|
ErrorReporter& _errorReporter
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!_errorLocation)
|
||||||
|
_errorLocation = &_declaration.location();
|
||||||
|
|
||||||
|
Declaration const* shadowedDeclaration = nullptr;
|
||||||
|
if (_warnOnShadow && !_declaration.name().empty())
|
||||||
|
for (auto const* decl: _container.resolveName(_declaration.name(), true))
|
||||||
|
if (decl != &_declaration)
|
||||||
|
{
|
||||||
|
shadowedDeclaration = decl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract()))
|
||||||
|
{
|
||||||
|
SourceLocation firstDeclarationLocation;
|
||||||
|
SourceLocation secondDeclarationLocation;
|
||||||
|
Declaration const* conflictingDeclaration = _container.conflictingDeclaration(_declaration, _name);
|
||||||
|
solAssert(conflictingDeclaration, "");
|
||||||
|
bool const comparable =
|
||||||
|
_errorLocation->sourceName &&
|
||||||
|
conflictingDeclaration->location().sourceName &&
|
||||||
|
*_errorLocation->sourceName == *conflictingDeclaration->location().sourceName;
|
||||||
|
if (comparable && _errorLocation->start < conflictingDeclaration->location().start)
|
||||||
|
{
|
||||||
|
firstDeclarationLocation = *_errorLocation;
|
||||||
|
secondDeclarationLocation = conflictingDeclaration->location();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
firstDeclarationLocation = conflictingDeclaration->location();
|
||||||
|
secondDeclarationLocation = *_errorLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
_errorReporter.declarationError(
|
||||||
|
secondDeclarationLocation,
|
||||||
|
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
|
||||||
|
"Identifier already declared."
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (shadowedDeclaration)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<MagicVariableDeclaration const*>(shadowedDeclaration))
|
||||||
|
_errorReporter.warning(
|
||||||
|
_declaration.location(),
|
||||||
|
"This declaration shadows a builtin symbol."
|
||||||
|
);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto shadowedLocation = shadowedDeclaration->location();
|
||||||
|
_errorReporter.warning(
|
||||||
|
_declaration.location(),
|
||||||
|
"This declaration shadows an existing declaration.",
|
||||||
|
SecondarySourceLocation().append("The shadowed declaration is here:", shadowedLocation)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool DeclarationRegistrationHelper::visit(SourceUnit& _sourceUnit)
|
bool DeclarationRegistrationHelper::visit(SourceUnit& _sourceUnit)
|
||||||
{
|
{
|
||||||
if (!m_scopes[&_sourceUnit])
|
if (!m_scopes[&_sourceUnit])
|
||||||
@ -590,30 +648,21 @@ void DeclarationRegistrationHelper::closeCurrentScope()
|
|||||||
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
|
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
|
||||||
{
|
{
|
||||||
solAssert(m_currentScope && m_scopes.count(m_currentScope), "No current scope.");
|
solAssert(m_currentScope && m_scopes.count(m_currentScope), "No current scope.");
|
||||||
if (!m_scopes[m_currentScope]->registerDeclaration(_declaration, nullptr, !_declaration.isVisibleInContract()))
|
|
||||||
{
|
|
||||||
SourceLocation firstDeclarationLocation;
|
|
||||||
SourceLocation secondDeclarationLocation;
|
|
||||||
Declaration const* conflictingDeclaration = m_scopes[m_currentScope]->conflictingDeclaration(_declaration);
|
|
||||||
solAssert(conflictingDeclaration, "");
|
|
||||||
|
|
||||||
if (_declaration.location().start < conflictingDeclaration->location().start)
|
bool warnAboutShadowing = true;
|
||||||
{
|
// Do not warn about shadowing for structs and enums because their members are
|
||||||
firstDeclarationLocation = _declaration.location();
|
// not accessible without prefixes.
|
||||||
secondDeclarationLocation = conflictingDeclaration->location();
|
if (
|
||||||
}
|
dynamic_cast<StructDefinition const*>(m_currentScope) ||
|
||||||
else
|
dynamic_cast<EnumDefinition const*>(m_currentScope)
|
||||||
{
|
)
|
||||||
firstDeclarationLocation = conflictingDeclaration->location();
|
warnAboutShadowing = false;
|
||||||
secondDeclarationLocation = _declaration.location();
|
// Do not warn about the constructor shadowing the contract.
|
||||||
}
|
if (auto fun = dynamic_cast<FunctionDefinition const*>(&_declaration))
|
||||||
|
if (fun->isConstructor())
|
||||||
|
warnAboutShadowing = false;
|
||||||
|
|
||||||
m_errorReporter.declarationError(
|
registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, m_errorReporter);
|
||||||
secondDeclarationLocation,
|
|
||||||
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
|
|
||||||
"Identifier already declared."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_declaration.setScope(m_currentScope);
|
_declaration.setScope(m_currentScope);
|
||||||
if (_opensScope)
|
if (_opensScope)
|
||||||
|
@ -136,6 +136,15 @@ public:
|
|||||||
ASTNode const* _currentScope = nullptr
|
ASTNode const* _currentScope = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static bool registerDeclaration(
|
||||||
|
DeclarationContainer& _container,
|
||||||
|
Declaration const& _declaration,
|
||||||
|
std::string const* _name,
|
||||||
|
SourceLocation const* _errorLocation,
|
||||||
|
bool _warnOnShadow,
|
||||||
|
ErrorReporter& _errorReporter
|
||||||
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool visit(SourceUnit& _sourceUnit) override;
|
bool visit(SourceUnit& _sourceUnit) override;
|
||||||
void endVisit(SourceUnit& _sourceUnit) override;
|
void endVisit(SourceUnit& _sourceUnit) override;
|
||||||
|
@ -42,11 +42,23 @@ void ErrorReporter::warning(string const& _description)
|
|||||||
error(Error::Type::Warning, SourceLocation(), _description);
|
error(Error::Type::Warning, SourceLocation(), _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::warning(SourceLocation const& _location, string const& _description)
|
void ErrorReporter::warning(
|
||||||
|
SourceLocation const& _location,
|
||||||
|
string const& _description
|
||||||
|
)
|
||||||
{
|
{
|
||||||
error(Error::Type::Warning, _location, _description);
|
error(Error::Type::Warning, _location, _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ErrorReporter::warning(
|
||||||
|
SourceLocation const& _location,
|
||||||
|
string const& _description,
|
||||||
|
SecondarySourceLocation const& _secondaryLocation
|
||||||
|
)
|
||||||
|
{
|
||||||
|
error(Error::Type::Warning, _location, _secondaryLocation, _description);
|
||||||
|
}
|
||||||
|
|
||||||
void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description)
|
void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
auto err = make_shared<Error>(_type);
|
auto err = make_shared<Error>(_type);
|
||||||
|
@ -41,29 +41,29 @@ public:
|
|||||||
|
|
||||||
ErrorReporter& operator=(ErrorReporter const& _errorReporter);
|
ErrorReporter& operator=(ErrorReporter const& _errorReporter);
|
||||||
|
|
||||||
void warning(std::string const& _description = std::string());
|
void warning(std::string const& _description);
|
||||||
|
|
||||||
|
void warning(SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
void warning(
|
void warning(
|
||||||
SourceLocation const& _location = SourceLocation(),
|
SourceLocation const& _location,
|
||||||
std::string const& _description = std::string()
|
std::string const& _description,
|
||||||
|
SecondarySourceLocation const& _secondaryLocation
|
||||||
);
|
);
|
||||||
|
|
||||||
void error(
|
void error(
|
||||||
Error::Type _type,
|
Error::Type _type,
|
||||||
SourceLocation const& _location = SourceLocation(),
|
SourceLocation const& _location,
|
||||||
std::string const& _description = std::string()
|
std::string const& _description
|
||||||
);
|
);
|
||||||
|
|
||||||
void declarationError(
|
void declarationError(
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(),
|
SecondarySourceLocation const& _secondaryLocation,
|
||||||
std::string const& _description = std::string()
|
std::string const& _description
|
||||||
);
|
);
|
||||||
|
|
||||||
void declarationError(
|
void declarationError(SourceLocation const& _location, std::string const& _description);
|
||||||
SourceLocation const& _location,
|
|
||||||
std::string const& _description = std::string()
|
|
||||||
);
|
|
||||||
|
|
||||||
void fatalDeclarationError(SourceLocation const& _location, std::string const& _description);
|
void fatalDeclarationError(SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
|
@ -20,11 +20,15 @@
|
|||||||
* Tests for high level features like import.
|
* Tests for high level features like import.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string>
|
#include <test/libsolidity/ErrorCheck.h>
|
||||||
#include <boost/test/unit_test.hpp>
|
|
||||||
#include <libsolidity/interface/Exceptions.h>
|
#include <libsolidity/interface/Exceptions.h>
|
||||||
#include <libsolidity/interface/CompilerStack.h>
|
#include <libsolidity/interface/CompilerStack.h>
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
@ -202,6 +206,67 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent)
|
|||||||
BOOST_CHECK(d.compile());
|
BOOST_CHECK(d.compile());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(shadowing_via_import)
|
||||||
|
{
|
||||||
|
CompilerStack c;
|
||||||
|
c.addSource("a", "library A {} pragma solidity >=0.0;");
|
||||||
|
c.addSource("b", "library A {} pragma solidity >=0.0;");
|
||||||
|
c.addSource("c", "import {A} from \"./a\"; import {A} from \"./b\";");
|
||||||
|
BOOST_CHECK(!c.compile());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(shadowing_builtins_with_imports)
|
||||||
|
{
|
||||||
|
CompilerStack c;
|
||||||
|
c.addSource("B.sol", "contract X {} pragma solidity >=0.0;");
|
||||||
|
c.addSource("b", R"(
|
||||||
|
pragma solidity >=0.0;
|
||||||
|
import * as msg from "B.sol";
|
||||||
|
contract C {
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
BOOST_CHECK(c.compile());
|
||||||
|
auto numErrors = c.errors().size();
|
||||||
|
// Sometimes we get the prerelease warning, sometimes not.
|
||||||
|
BOOST_CHECK(2 <= numErrors && numErrors <= 3);
|
||||||
|
for (auto const& e: c.errors())
|
||||||
|
{
|
||||||
|
string const* msg = e->comment();
|
||||||
|
BOOST_REQUIRE(msg);
|
||||||
|
BOOST_CHECK(
|
||||||
|
msg->find("pre-release") != string::npos ||
|
||||||
|
msg->find("shadows a builtin symbol") != string::npos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(shadowing_builtins_with_multiple_imports)
|
||||||
|
{
|
||||||
|
CompilerStack c;
|
||||||
|
c.addSource("B.sol", "contract msg {} contract block{} pragma solidity >=0.0;");
|
||||||
|
c.addSource("b", R"(
|
||||||
|
pragma solidity >=0.0;
|
||||||
|
import {msg, block} from "B.sol";
|
||||||
|
contract C {
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
BOOST_CHECK(c.compile());
|
||||||
|
auto numErrors = c.errors().size();
|
||||||
|
// Sometimes we get the prerelease warning, sometimes not.
|
||||||
|
BOOST_CHECK(4 <= numErrors && numErrors <= 5);
|
||||||
|
for (auto const& e: c.errors())
|
||||||
|
{
|
||||||
|
string const* msg = e->comment();
|
||||||
|
BOOST_REQUIRE(msg);
|
||||||
|
BOOST_CHECK(
|
||||||
|
msg->find("pre-release") != string::npos ||
|
||||||
|
msg->find("shadows a builtin symbol") != string::npos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5498,22 +5498,6 @@ BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_library)
|
|||||||
CHECK_SUCCESS_NO_WARNINGS(text);
|
CHECK_SUCCESS_NO_WARNINGS(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(does_not_warn_non_magic_msg_value)
|
|
||||||
{
|
|
||||||
char const* text = R"(
|
|
||||||
contract C {
|
|
||||||
struct msg {
|
|
||||||
uint256 value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function f() {
|
|
||||||
msg.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
CHECK_SUCCESS_NO_WARNINGS(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_modifier_following_non_payable_public_function)
|
BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_modifier_following_non_payable_public_function)
|
||||||
{
|
{
|
||||||
char const* text = R"(
|
char const* text = R"(
|
||||||
@ -6127,6 +6111,85 @@ BOOST_AUTO_TEST_CASE(no_unused_inline_asm)
|
|||||||
CHECK_SUCCESS_NO_WARNINGS(text);
|
CHECK_SUCCESS_NO_WARNINGS(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(shadowing_builtins_with_functions)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function keccak256() {}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_WARNING(text, "shadows a builtin symbol");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(shadowing_builtins_with_variables)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
uint msg;
|
||||||
|
msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_WARNING(text, "shadows a builtin symbol");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(shadowing_builtins_with_parameters)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f(uint require) {
|
||||||
|
require = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_WARNING(text, "shadows a builtin symbol");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(shadowing_builtins_with_return_parameters)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() returns (uint require) {
|
||||||
|
require = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_WARNING(text, "shadows a builtin symbol");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(shadowing_builtins_with_events)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
event keccak256();
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_WARNING(text, "shadows a builtin symbol");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(shadowing_builtins_ignores_struct)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
struct a {
|
||||||
|
uint msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_SUCCESS_NO_WARNINGS(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(shadowing_builtins_ignores_constructor)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function C() {}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
CHECK_SUCCESS_NO_WARNINGS(text);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(callable_crash)
|
BOOST_AUTO_TEST_CASE(callable_crash)
|
||||||
{
|
{
|
||||||
char const* text = R"(
|
char const* text = R"(
|
||||||
@ -6245,14 +6308,6 @@ BOOST_AUTO_TEST_CASE(create2_as_variable)
|
|||||||
CHECK_WARNING_ALLOW_MULTI(text, "Variable is shadowed in inline assembly by an instruction of the same name");
|
CHECK_WARNING_ALLOW_MULTI(text, "Variable is shadowed in inline assembly by an instruction of the same name");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(shadowing_warning_can_be_removed)
|
|
||||||
{
|
|
||||||
char const* text = R"(
|
|
||||||
contract C {function f() {assembly {}}}
|
|
||||||
)";
|
|
||||||
CHECK_SUCCESS_NO_WARNINGS(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(warn_unspecified_storage)
|
BOOST_AUTO_TEST_CASE(warn_unspecified_storage)
|
||||||
{
|
{
|
||||||
char const* text = R"(
|
char const* text = R"(
|
||||||
|
Loading…
Reference in New Issue
Block a user