diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 3a83058c1..5b85989b9 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -382,6 +382,27 @@ vector, FunctionTypePointer>> const& ContractDefinition::getIn return *m_interfaceFunctionList; } +string const& ContractDefinition::devDocumentation() const +{ + return m_devDocumentation; +} + +string const& ContractDefinition::userDocumentation() const +{ + return m_userDocumentation; +} + +void ContractDefinition::setDevDocumentation(string const& _devDocumentation) +{ + m_devDocumentation = _devDocumentation; +} + +void ContractDefinition::setUserDocumentation(string const& _userDocumentation) +{ + m_userDocumentation = _userDocumentation; +} + + vector const& ContractDefinition::getInheritableMembers() const { if (!m_inheritableMembers) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 658b3de9f..fb83d4e11 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -281,6 +281,12 @@ public: /// Returns the fallback function or nullptr if no fallback function was specified. FunctionDefinition const* getFallbackFunction() const; + std::string const& userDocumentation() const; + void setUserDocumentation(std::string const& _userDocumentation); + + std::string const& devDocumentation() const; + void setDevDocumentation(std::string const& _devDocumentation); + private: /// Checks that two functions defined in this contract with the same name have different /// arguments and that there is at most one constructor. @@ -302,6 +308,10 @@ private: std::vector> m_functionModifiers; std::vector> m_events; + // parsed Natspec documentation of the contract. + std::string m_userDocumentation; + std::string m_devDocumentation; + std::vector m_linearizedBaseContracts; mutable std::unique_ptr, FunctionTypePointer>>> m_interfaceFunctionList; mutable std::unique_ptr>> m_interfaceEvents; diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index a3399823e..f056bb9b1 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -116,6 +116,7 @@ void CompilerStack::parse() resolver.resolveNamesAndTypes(*contract); m_contracts[contract->getName()].contract = contract; } + InterfaceHandler interfaceHandler; for (Source const* source: m_sourceOrder) for (ASTPointer const& node: source->ast->getNodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) @@ -123,6 +124,8 @@ void CompilerStack::parse() m_globalContext->setCurrentContract(*contract); resolver.updateDeclaration(*m_globalContext->getCurrentThis()); resolver.checkTypeRequirements(*contract); + contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract)); + contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract)); m_contracts[contract->getName()].contract = contract; } m_parseSuccessful = true; @@ -231,6 +234,8 @@ string const& CompilerStack::getMetadata(string const& _contractName, Documentat Contract const& contract = getContract(_contractName); std::unique_ptr* doc; + + // checks wheather we already have the documentation switch (_type) { case DocumentationType::NatspecUser: @@ -248,8 +253,11 @@ string const& CompilerStack::getMetadata(string const& _contractName, Documentat default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); } + + // caches the result if (!*doc) - *doc = contract.interfaceHandler->getDocumentation(*contract.contract, _type); + doc->reset(new string(contract.interfaceHandler->getDocumentation(*contract.contract, _type))); + return *(*doc); } diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 23b7ff82f..c6f8553d8 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -16,7 +16,7 @@ InterfaceHandler::InterfaceHandler() m_lastTag = DocTagType::None; } -unique_ptr InterfaceHandler::getDocumentation( +string InterfaceHandler::getDocumentation( ContractDefinition const& _contractDef, DocumentationType _type ) @@ -24,9 +24,9 @@ unique_ptr InterfaceHandler::getDocumentation( switch(_type) { case DocumentationType::NatspecUser: - return getUserDocumentation(_contractDef); + return userDocumentation(_contractDef); case DocumentationType::NatspecDev: - return getDevDocumentation(_contractDef); + return devDocumentation(_contractDef); case DocumentationType::ABIInterface: return getABIInterface(_contractDef); case DocumentationType::ABISolidityInterface: @@ -34,10 +34,10 @@ unique_ptr InterfaceHandler::getDocumentation( } BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type")); - return nullptr; + return ""; } -unique_ptr InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef) +string InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef) { Json::Value abi(Json::arrayValue); @@ -103,10 +103,10 @@ unique_ptr InterfaceHandler::getABIInterface(ContractDefinition const& _ event["inputs"] = params; abi.append(event); } - return unique_ptr(new string(Json::FastWriter().write(abi))); + return Json::FastWriter().write(abi); } -unique_ptr InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef) +string InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef) { string ret = "contract " + _contractDef.getName() + "{"; @@ -140,10 +140,10 @@ unique_ptr InterfaceHandler::getABISolidityInterface(ContractDefinition ret += ";"; } - return unique_ptr(new string(ret + "}")); + return ret + "}"; } -unique_ptr InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef) +string InterfaceHandler::userDocumentation(ContractDefinition const& _contractDef) { Json::Value doc; Json::Value methods(Json::objectValue); @@ -165,10 +165,10 @@ unique_ptr InterfaceHandler::getUserDocumentation(ContractDefinition con } doc["methods"] = methods; - return unique_ptr(new string(Json::FastWriter().write(doc))); + return Json::StyledWriter().write(doc); } -unique_ptr InterfaceHandler::getDevDocumentation(ContractDefinition const& _contractDef) +string InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef) { // LTODO: Somewhere in this function warnings for mismatch of param names // should be thrown @@ -212,7 +212,7 @@ unique_ptr InterfaceHandler::getDevDocumentation(ContractDefinition cons // LTODO: mismatching parameter name, throw some form of warning and not just an exception BOOST_THROW_EXCEPTION( DocstringParsingError() << - errinfo_comment("documented parameter \"" + pair.first + "\" not found found in the function") + errinfo_comment("documented parameter \"" + pair.first + "\" not found in the parameter list of the function.") ); params[pair.first] = pair.second; } @@ -229,7 +229,7 @@ unique_ptr InterfaceHandler::getDevDocumentation(ContractDefinition cons } doc["methods"] = methods; - return unique_ptr(new string(Json::FastWriter().write(doc))); + return Json::StyledWriter().write(doc); } /* -- private -- */ diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index ca9807d7e..7784dbd7f 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/InterfaceHandler.h @@ -65,28 +65,25 @@ public: /// @param _contractDef The contract definition /// @param _type The type of the documentation. Can be one of the /// types provided by @c DocumentationType - /// @return A unique pointer contained string with the json - /// representation of provided type - std::unique_ptr getDocumentation( + /// @return A string with the json representation of provided type + std::string getDocumentation( ContractDefinition const& _contractDef, DocumentationType _type ); /// Get the ABI Interface of the contract /// @param _contractDef The contract definition - /// @return A unique pointer contained string with the json - /// representation of the contract's ABI Interface - std::unique_ptr getABIInterface(ContractDefinition const& _contractDef); - std::unique_ptr getABISolidityInterface(ContractDefinition const& _contractDef); + /// @return A string with the json representation of the contract's ABI Interface + std::string getABIInterface(ContractDefinition const& _contractDef); + std::string getABISolidityInterface(ContractDefinition const& _contractDef); /// Get the User documentation of the contract /// @param _contractDef The contract definition - /// @return A unique pointer contained string with the json - /// representation of the contract's user documentation - std::unique_ptr getUserDocumentation(ContractDefinition const& _contractDef); - /// Get the Developer's documentation of the contract + /// @return A string with the json representation of the contract's user documentation + std::string userDocumentation(ContractDefinition const& _contractDef); + /// Genereates the Developer's documentation of the contract /// @param _contractDef The contract definition - /// @return A unique pointer contained string with the json - /// representation of the contract's developer documentation - std::unique_ptr getDevDocumentation(ContractDefinition const& _contractDef); + /// @return A string with the json representation + /// of the contract's developer documentation + std::string devDocumentation(ContractDefinition const& _contractDef); private: void resetUser(); diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 548b7dc21..fbf8478df 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -26,6 +26,7 @@ #include #include #include +#include using namespace std; diff --git a/mix/CodeModel.cpp b/mix/CodeModel.cpp index 082745cfe..213c1c1ee 100644 --- a/mix/CodeModel.cpp +++ b/mix/CodeModel.cpp @@ -157,7 +157,7 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler m_bytes = _compiler.getBytecode(_contractName.toStdString()); dev::solidity::InterfaceHandler interfaceHandler; - m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition)); + m_contractInterface = QString::fromStdString(interfaceHandler.getABIInterface(contractDefinition)); if (m_contractInterface.isEmpty()) m_contractInterface = "[]"; if (contractDefinition.getLocation().sourceName.get()) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 00bf2ab33..1b1dbd3eb 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -449,6 +449,11 @@ bool CommandLineInterface::processInput() << boost::diagnostic_information(_exception); return false; } + catch (DocstringParsingError const& _exception) + { + cerr << "Documentation parsing error: " << *boost::get_error_info(_exception) << endl; + return false; + } catch (Exception const& _exception) { cerr << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl; diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index 340713b1d..bde13762a 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -147,6 +147,10 @@ string compile(string _input, bool _optimize) { return formatError(exception, "Internal compiler error", compiler); } + catch (DocstringParsingError const& exception) + { + return formatError(exception, "Documentation parsing error", compiler); + } catch (Exception const& exception) { output["error"] = "Exception during compilation: " + boost::diagnostic_information(exception); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f4b74a37d..d44545145 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include using namespace std; @@ -4657,6 +4658,32 @@ BOOST_AUTO_TEST_CASE(bytes_memory_index_access) ) == encodeArgs(u256(data.size()), string("d"))); } +BOOST_AUTO_TEST_CASE(dev_title_at_function_error) +{ + char const* sourceCode = " /// @author Lefteris\n" + " /// @title Just a test contract\n" + "contract test {\n" + " /// @dev Mul function\n" + " /// @title I really should not be here\n" + " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + "}\n"; + + compileRequireThrow(sourceCode); +} + +BOOST_AUTO_TEST_CASE(dev_documenting_nonexistant_param) +{ + char const* sourceCode = "contract test {\n" + " /// @dev Multiplies a number by 7 and adds second parameter\n" + " /// @param a Documentation for the first parameter\n" + " /// @param not_existing Documentation for the second parameter\n" + " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + "}\n"; + + compileRequireThrow(sourceCode); +} + + BOOST_AUTO_TEST_CASE(storage_array_ref) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index d2c1ec186..73c080f70 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -21,6 +21,7 @@ */ #include "../TestHelper.h" +#include #include #include #include @@ -38,9 +39,11 @@ class DocumentationChecker public: DocumentationChecker(): m_compilerStack(false) {} - void checkNatspec(std::string const& _code, - std::string const& _expectedDocumentationString, - bool _userDocumentation) + void checkNatspec( + std::string const& _code, + std::string const& _expectedDocumentationString, + bool _userDocumentation + ) { std::string generatedDocumentationString; ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing failed"); @@ -53,9 +56,11 @@ public: m_reader.parse(generatedDocumentationString, generatedDocumentation); Json::Value expectedDocumentation; m_reader.parse(_expectedDocumentationString, expectedDocumentation); - BOOST_CHECK_MESSAGE(expectedDocumentation == generatedDocumentation, - "Expected " << _expectedDocumentationString << - "\n but got:\n" << generatedDocumentationString); + BOOST_CHECK_MESSAGE( + expectedDocumentation == generatedDocumentation, + "Expected " << _expectedDocumentationString << + "\n but got:\n" << generatedDocumentationString + ); } private: @@ -229,18 +234,6 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params) checkNatspec(sourceCode, natspec, false); } -BOOST_AUTO_TEST_CASE(dev_documenting_nonexistant_param) -{ - char const* sourceCode = "contract test {\n" - " /// @dev Multiplies a number by 7 and adds second parameter\n" - " /// @param a Documentation for the first parameter\n" - " /// @param not_existing Documentation for the second parameter\n" - " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" - "}\n"; - - BOOST_CHECK_THROW(checkNatspec(sourceCode, "", false), DocstringParsingError); -} - BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) { char const* sourceCode = "contract test {\n" @@ -488,46 +481,48 @@ BOOST_AUTO_TEST_CASE(dev_author_at_function) checkNatspec(sourceCode, natspec, false); } -BOOST_AUTO_TEST_CASE(dev_title_at_function_error) -{ - char const* sourceCode = " /// @author Lefteris\n" - " /// @title Just a test contract\n" - "contract test {\n" - " /// @dev Mul function\n" - " /// @title I really should not be here\n" - " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" - "}\n"; - - BOOST_CHECK_THROW(checkNatspec(sourceCode, "", false), DocstringParsingError); -} - BOOST_AUTO_TEST_CASE(natspec_notice_without_tag) { - char const* sourceCode = "contract test {\n" - " /// I do something awesome\n" - " function mul(uint a) returns(uint d) { return a * 7; }\n" - "}\n"; + char const* sourceCode = R"( + contract test { + /// I do something awesome + function mul(uint a) returns(uint d) { return a * 7; } + } + )"; - char const* natspec = "{" - "\"methods\":{" - " \"mul(uint256)\":{ \"notice\": \"I do something awesome\"}" - "}}"; + + char const* natspec = R"ABCDEF( + { + "methods" : { + "mul(uint256)" : { + "notice" : "I do something awesome" + } + } + } + )ABCDEF"; checkNatspec(sourceCode, natspec, true); } BOOST_AUTO_TEST_CASE(natspec_multiline_notice_without_tag) { - char const* sourceCode = "contract test {\n" - " /// I do something awesome\n" - " /// which requires two lines to explain\n" - " function mul(uint a) returns(uint d) { return a * 7; }\n" - "}\n"; - - char const* natspec = "{" - "\"methods\":{" - " \"mul(uint256)\":{ \"notice\": \"I do something awesome which requires two lines to explain\"}" - "}}"; + char const* sourceCode = R"( + contract test { + /// I do something awesome + /// which requires two lines to explain + function mul(uint a) returns(uint d) { return a * 7; } + } + )"; + + char const* natspec = R"ABCDEF( + { + "methods" : { + "mul(uint256)" : { + "notice" : "I do something awesome which requires two lines to explain" + } + } + } + )ABCDEF"; checkNatspec(sourceCode, natspec, true); } diff --git a/test/libsolidity/solidityExecutionFramework.h b/test/libsolidity/solidityExecutionFramework.h index 03a201c72..f4dbbcb97 100644 --- a/test/libsolidity/solidityExecutionFramework.h +++ b/test/libsolidity/solidityExecutionFramework.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace dev { @@ -57,6 +58,14 @@ public: return m_output; } + template + void compileRequireThrow(std::string const& _sourceCode) + { + m_compiler.reset(false, m_addStandardSources); + m_compiler.addSource("", _sourceCode); + BOOST_REQUIRE_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), Exceptiontype); + } + bytes const& compileAndRun( std::string const& _sourceCode, u256 const& _value = 0,