diff options
author | Alex Beregszaszi <alex@rtfs.hu> | 2017-08-05 02:46:09 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-05 02:46:09 +0800 |
commit | a372941a442fe1029d212ebf7b097bdea7534fad (patch) | |
tree | fe00baa257d974c65545886c266bf7348cd0cbf2 | |
parent | f3af014afd1e6e70ab25ea30bff6f272cc73b0a9 (diff) | |
parent | 2186401479d0d41b5fb45d7316a479c1c9bfa7ad (diff) | |
download | dexon-solidity-a372941a442fe1029d212ebf7b097bdea7534fad.tar.gz dexon-solidity-a372941a442fe1029d212ebf7b097bdea7534fad.tar.zst dexon-solidity-a372941a442fe1029d212ebf7b097bdea7534fad.zip |
Merge pull request #2687 from ethereum/show-unimplemented-funcs
Show unimplemented function if trying to instantiate an abstract class
-rw-r--r-- | Changelog.md | 1 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 28 | ||||
-rw-r--r-- | libsolidity/ast/ASTAnnotations.h | 5 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 2 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 2 | ||||
-rw-r--r-- | libsolidity/interface/ErrorReporter.cpp | 10 | ||||
-rw-r--r-- | libsolidity/interface/ErrorReporter.h | 6 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 16 |
8 files changed, 46 insertions, 24 deletions
diff --git a/Changelog.md b/Changelog.md index 7d3369f3..f7eb8070 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.15 (unreleased) Features: + * Type Checker: Show unimplemented function if trying to instantiate an abstract class. Bugfixes: * Code Generator: ``.delegatecall()`` should always return execution outcome. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 5fe03aa6..ce4baa3f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -112,8 +112,6 @@ bool TypeChecker::visit(ContractDefinition const& _contract) m_errorReporter.typeError(fallbackFunction->returnParameterList()->location(), "Fallback function cannot return values."); } } - if (!function->isImplemented()) - _contract.annotation().isFullyImplemented = false; } for (auto const& n: _contract.subNodes()) @@ -188,7 +186,6 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>; map<string, vector<FunTypeAndFlag>> functions; - bool allBaseConstructorsImplemented = true; // Search from base to derived for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) for (FunctionDefinition const* function: contract->definedFunctions()) @@ -199,7 +196,7 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont if (!function->isImplemented()) // Base contract's constructor is not fully implemented, no way to get // out of this. - allBaseConstructorsImplemented = false; + _contract.annotation().unimplementedFunctions.push_back(function); continue; } auto& overloads = functions[function->name()]; @@ -219,16 +216,15 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont it->second = true; } - if (!allBaseConstructorsImplemented) - _contract.annotation().isFullyImplemented = false; - // Set to not fully implemented if at least one flag is false. for (auto const& it: functions) for (auto const& funAndFlag: it.second) if (!funAndFlag.second) { - _contract.annotation().isFullyImplemented = false; - return; + FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(&funAndFlag.first->declaration()); + solAssert(function, ""); + _contract.annotation().unimplementedFunctions.push_back(function); + break; } } @@ -266,7 +262,8 @@ void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _c } } if (!argumentsNeeded.empty()) - _contract.annotation().isFullyImplemented = false; + for (ContractDefinition const* contract: argumentsNeeded) + _contract.annotation().unimplementedFunctions.push_back(contract->constructor()); } void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract) @@ -1523,8 +1520,15 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (!contract) m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract."); - if (!contract->annotation().isFullyImplemented) - m_errorReporter.typeError(_newExpression.location(), "Trying to create an instance of an abstract contract."); + if (!contract->annotation().unimplementedFunctions.empty()) + m_errorReporter.typeError( + _newExpression.location(), + SecondarySourceLocation().append( + "Missing implementation:", + contract->annotation().unimplementedFunctions.front()->location() + ), + "Trying to create an instance of an abstract contract." + ); if (!contract->constructorIsPublic()) m_errorReporter.typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly."); diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 45a6dd1a..ecddbe21 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -79,8 +79,9 @@ struct TypeDeclarationAnnotation: ASTAnnotation struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnotation { - /// Whether all functions are implemented. - bool isFullyImplemented = true; + /// List of functions without a body. Can also contain functions from base classes, + /// especially constructors. + std::vector<FunctionDefinition const*> unimplementedFunctions; /// List of all (direct and indirect) base contracts in order from derived to /// base, including the contract itself. std::vector<ContractDefinition const*> linearizedBaseContracts; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index eda70b63..e4a602cb 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -253,7 +253,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node) make_pair("name", _node.name()), make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), make_pair("contractKind", contractKind(_node.contractKind())), - make_pair("fullyImplemented", _node.annotation().isFullyImplemented), + make_pair("fullyImplemented", _node.annotation().unimplementedFunctions.empty()), make_pair("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)), make_pair("baseContracts", toJson(_node.baseContracts())), make_pair("contractDependencies", getContainerIds(_node.annotation().contractDependencies)), diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 9689b700..7c66a843 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -639,7 +639,7 @@ void CompilerStack::compileContract( { if ( _compiledContracts.count(&_contract) || - !_contract.annotation().isFullyImplemented || + !_contract.annotation().unimplementedFunctions.empty() || !_contract.constructorIsPublic() ) return; diff --git a/libsolidity/interface/ErrorReporter.cpp b/libsolidity/interface/ErrorReporter.cpp index f9ef4ceb..e6171756 100644 --- a/libsolidity/interface/ErrorReporter.cpp +++ b/libsolidity/interface/ErrorReporter.cpp @@ -151,6 +151,16 @@ void ErrorReporter::syntaxError(SourceLocation const& _location, string const& _ ); } +void ErrorReporter::typeError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) +{ + error( + Error::Type::TypeError, + _location, + _secondaryLocation, + _description + ); +} + void ErrorReporter::typeError(SourceLocation const& _location, string const& _description) { error( diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h index 8b066a3e..42b0c8b6 100644 --- a/libsolidity/interface/ErrorReporter.h +++ b/libsolidity/interface/ErrorReporter.h @@ -73,6 +73,12 @@ public: void syntaxError(SourceLocation const& _location, std::string const& _description); + void typeError( + SourceLocation const& _location, + SecondarySourceLocation const& _secondaryLocation, + std::string const& _description + ); + void typeError(SourceLocation const& _location, std::string const& _description); void fatalTypeError(SourceLocation const& _location, std::string const& _description); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 4d367d3a..37bbc2eb 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -624,7 +624,7 @@ BOOST_AUTO_TEST_CASE(function_no_implementation) std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[1].get()); BOOST_REQUIRE(contract); - BOOST_CHECK(!contract->annotation().isFullyImplemented); + BOOST_CHECK(!contract->annotation().unimplementedFunctions.empty()); BOOST_CHECK(!contract->definedFunctions()[0]->isImplemented()); } @@ -640,10 +640,10 @@ BOOST_AUTO_TEST_CASE(abstract_contract) ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[1].get()); ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get()); BOOST_REQUIRE(base); - BOOST_CHECK(!base->annotation().isFullyImplemented); + BOOST_CHECK(!base->annotation().unimplementedFunctions.empty()); BOOST_CHECK(!base->definedFunctions()[0]->isImplemented()); BOOST_REQUIRE(derived); - BOOST_CHECK(derived->annotation().isFullyImplemented); + BOOST_CHECK(derived->annotation().unimplementedFunctions.empty()); BOOST_CHECK(derived->definedFunctions()[0]->isImplemented()); } @@ -659,9 +659,9 @@ BOOST_AUTO_TEST_CASE(abstract_contract_with_overload) ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[1].get()); ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get()); BOOST_REQUIRE(base); - BOOST_CHECK(!base->annotation().isFullyImplemented); + BOOST_CHECK(!base->annotation().unimplementedFunctions.empty()); BOOST_REQUIRE(derived); - BOOST_CHECK(!derived->annotation().isFullyImplemented); + BOOST_CHECK(!derived->annotation().unimplementedFunctions.empty()); } BOOST_AUTO_TEST_CASE(create_abstract_contract) @@ -693,7 +693,7 @@ BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_optional) BOOST_CHECK_EQUAL(nodes.size(), 4); ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[3].get()); BOOST_REQUIRE(derived); - BOOST_CHECK(!derived->annotation().isFullyImplemented); + BOOST_CHECK(!derived->annotation().unimplementedFunctions.empty()); } BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_not_provided) @@ -712,7 +712,7 @@ BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_not_provided) BOOST_CHECK_EQUAL(nodes.size(), 4); ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[3].get()); BOOST_REQUIRE(derived); - BOOST_CHECK(!derived->annotation().isFullyImplemented); + BOOST_CHECK(!derived->annotation().unimplementedFunctions.empty()); } BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract) @@ -738,7 +738,7 @@ BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor) BOOST_CHECK_EQUAL(nodes.size(), 3); ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get()); BOOST_REQUIRE(derived); - BOOST_CHECK(!derived->annotation().isFullyImplemented); + BOOST_CHECK(!derived->annotation().unimplementedFunctions.empty()); } BOOST_AUTO_TEST_CASE(function_canonical_signature) |