From 60ac4d3a2c3960edec7db7b9e1328c2597c406e9 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 1 Dec 2014 17:03:04 +0100 Subject: [PATCH 01/26] Exporting Natspec documentation to a JSON interface - Adding a getDocumentation() function to solidity compiler stack so that we can obtain the natspec interface for a contract - Adding libjsoncpp as a dependency of libsolidity. This is done in a dirty way, using libjsonrpc-cpp s an intermediate dependency for the moment. Will fix soon. - Start of a test file for Natspec exporting to JSON --- libsolidity/AST.h | 2 +- libsolidity/CMakeLists.txt | 4 ++ libsolidity/CompilerStack.cpp | 26 ++++++++++++ libsolidity/CompilerStack.h | 4 ++ test/solidityNatspecJSON.cpp | 77 +++++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 test/solidityNatspecJSON.cpp diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 81a12ad1a..4ed8489fc 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -199,7 +199,7 @@ public: Block& getBody() { return *m_body; } /// @return A shared pointer of an ASTString. /// Can contain a nullptr in which case indicates absence of documentation - ASTPointer const& getDocumentation() { return m_documentation; } + ASTPointer const& getDocumentation() const { return m_documentation; } void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); } std::vector const& getLocalVariables() const { return m_localVariables; } diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index ea2ef4b74..b5147ced3 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -16,6 +16,10 @@ endif() include_directories(..) target_link_libraries(${EXECUTABLE} evmcore devcore) +# TODO: Temporary until PR 532 https://github.com/ethereum/cpp-ethereum/pull/532 +# gets accepted. Then we can simply add jsoncpp as a dependency and not the +# whole of JSONRPC as we are doing right here +target_link_libraries(${EXECUTABLE} ${JSONRPC_LS}) install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 6535e00d4..45d3e0b8a 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -28,6 +28,8 @@ #include #include +#include + using namespace std; namespace dev @@ -125,6 +127,30 @@ string const& CompilerStack::getInterface() return m_interface; } +string const& CompilerStack::getDocumentation() +{ + + Json::StyledWriter writer; + if (!m_parseSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + if (m_documentation.empty()) + { + Json::Value doc; + Json::Value methods; + + vector exportedFunctions = m_contractASTNode->getInterfaceFunctions(); + for (FunctionDefinition const* f: exportedFunctions) + { + Json::Value user; + user["user"] = Json::Value(*f->getDocumentation()); + methods[f->getName()] = user; + } + doc["methods"] = methods; + m_documentation = writer.write(doc); + } + return m_documentation; +} + bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) { CompilerStack stack; diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 6cae8660f..74784c5ef 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -62,6 +62,9 @@ public: /// Returns a string representing the contract interface in JSON. /// Prerequisite: Successful call to parse or compile. std::string const& getInterface(); + /// Returns a string representing the contract documentation in JSON. + /// Prerequisite: Successful call to parse or compile. + std::string const& getDocumentation(); /// Returns the previously used scanner, useful for counting lines during error reporting. Scanner const& getScanner() const { return *m_scanner; } @@ -77,6 +80,7 @@ private: std::shared_ptr m_contractASTNode; bool m_parseSuccessful; std::string m_interface; + std::string m_documentation; std::shared_ptr m_compiler; bytes m_bytecode; }; diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp new file mode 100644 index 000000000..5918eec99 --- /dev/null +++ b/test/solidityNatspecJSON.cpp @@ -0,0 +1,77 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** + * @author Lefteris Karapetsas + * @date 2014 + * Unit tests for the solidity compiler JSON Interface output. + */ + +#include +#include +#include + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +class DocumentationChecker +{ +public: + void checkNatspec(std::string const& _code, std::string const& _expectedDocumentationString) + { + m_compilerStack.parse(_code); + auto generatedDocumentationString = m_compilerStack.getDocumentation(); + Json::Value generatedDocumentation; + 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); + } + +private: + CompilerStack m_compilerStack; + Json::Reader m_reader; +}; + +BOOST_FIXTURE_TEST_SUITE(SolidityNatspecJSON, DocumentationChecker) + +BOOST_AUTO_TEST_CASE(basic_test) +{ + char const* sourceCode = "contract test {\n" + " /// Multiplies `a` by 7\n" + " function mul(uint a) returns(uint d) { return a * 7; }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul\":{ \"user\": \" Multiplies `a` by 7\"}" + "}}"; + + checkNatspec(sourceCode, natspec); +} + + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} From eed32824c3eb5ebd271d7cff585655b48ab5f697 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 1 Dec 2014 18:01:42 +0100 Subject: [PATCH 02/26] Using jsoncpp for exporting ABI interface from solidity - Also changing the interface JSON test to have a shorter name plus to provide meaningful error message in case of failure --- libsolidity/CompilerStack.cpp | 48 ++++++++++++++---------------- test/solidityJSONInterfaceTest.cpp | 26 ++++++++-------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 45d3e0b8a..e25438f7c 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -85,54 +85,52 @@ void CompilerStack::streamAssembly(ostream& _outStream) string const& CompilerStack::getInterface() { + Json::StyledWriter writer; if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + if (m_interface.empty()) { - stringstream interface; - interface << '['; + Json::Value methods(Json::arrayValue); + vector exportedFunctions = m_contractASTNode->getInterfaceFunctions(); - unsigned functionsCount = exportedFunctions.size(); for (FunctionDefinition const* f: exportedFunctions) { - auto streamVariables = [&](vector> const& _vars) + Json::Value method; + Json::Value inputs(Json::arrayValue); + Json::Value outputs(Json::arrayValue); + + auto streamVariables = [&](vector> const& _vars, + Json::Value &json) { - unsigned varCount = _vars.size(); for (ASTPointer const& var: _vars) { - interface << "{" - << "\"name\":" << escaped(var->getName(), false) << "," - << "\"type\":" << escaped(var->getType()->toString(), false) - << "}"; - if (--varCount > 0) - interface << ","; + Json::Value input; + input["name"] = var->getName(); + input["type"] = var->getType()->toString(); + json.append(input); } }; - interface << '{' - << "\"name\":" << escaped(f->getName(), false) << "," - << "\"inputs\":["; - streamVariables(f->getParameters()); - interface << "]," - << "\"outputs\":["; - streamVariables(f->getReturnParameters()); - interface << "]" - << "}"; - if (--functionsCount > 0) - interface << ","; + method["name"] = f->getName(); + streamVariables(f->getParameters(), inputs); + method["inputs"] = inputs; + streamVariables(f->getReturnParameters(), outputs); + method["outputs"] = outputs; + + methods.append(method); } - interface << ']'; - m_interface = interface.str(); + m_interface = writer.write(methods); } return m_interface; } string const& CompilerStack::getDocumentation() { - Json::StyledWriter writer; if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + if (m_documentation.empty()) { Json::Value doc; diff --git a/test/solidityJSONInterfaceTest.cpp b/test/solidityJSONInterfaceTest.cpp index 1a443087f..b91880130 100644 --- a/test/solidityJSONInterfaceTest.cpp +++ b/test/solidityJSONInterfaceTest.cpp @@ -34,7 +34,7 @@ namespace test class InterfaceChecker { public: - bool checkInterface(std::string const& _code, std::string const& _expectedInterfaceString) + void checkInterface(std::string const& _code, std::string const& _expectedInterfaceString) { m_compilerStack.parse(_code); std::string generatedInterfaceString = m_compilerStack.getInterface(); @@ -42,15 +42,17 @@ public: m_reader.parse(generatedInterfaceString, generatedInterface); Json::Value expectedInterface; m_reader.parse(_expectedInterfaceString, expectedInterface); - return expectedInterface == generatedInterface; + BOOST_CHECK_MESSAGE(expectedInterface == generatedInterface, + "Expected " << _expectedInterfaceString << + "\n but got:\n" << generatedInterfaceString); } - + private: CompilerStack m_compilerStack; Json::Reader m_reader; }; -BOOST_FIXTURE_TEST_SUITE(SolidityCompilerJSONInterfaceOutput, InterfaceChecker) +BOOST_FIXTURE_TEST_SUITE(solidityABIJSON, InterfaceChecker) BOOST_AUTO_TEST_CASE(basic_test) { @@ -76,7 +78,7 @@ BOOST_AUTO_TEST_CASE(basic_test) } ])"; - BOOST_CHECK(checkInterface(sourceCode, interface)); + checkInterface(sourceCode, interface); } BOOST_AUTO_TEST_CASE(empty_contract) @@ -86,7 +88,7 @@ BOOST_AUTO_TEST_CASE(empty_contract) char const* interface = "[]"; - BOOST_CHECK(checkInterface(sourceCode, interface)); + checkInterface(sourceCode, interface); } BOOST_AUTO_TEST_CASE(multiple_methods) @@ -95,7 +97,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods) " function f(uint a) returns(uint d) { return a * 7; }\n" " function g(uint b) returns(uint e) { return b * 8; }\n" "}\n"; - + char const* interface = R"([ { "name": "f", @@ -129,7 +131,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods) } ])"; - BOOST_CHECK(checkInterface(sourceCode, interface)); + checkInterface(sourceCode, interface); } BOOST_AUTO_TEST_CASE(multiple_params) @@ -160,7 +162,7 @@ BOOST_AUTO_TEST_CASE(multiple_params) } ])"; - BOOST_CHECK(checkInterface(sourceCode, interface)); + checkInterface(sourceCode, interface); } BOOST_AUTO_TEST_CASE(multiple_methods_order) @@ -170,7 +172,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) " function f(uint a) returns(uint d) { return a * 7; }\n" " function c(uint b) returns(uint e) { return b * 8; }\n" "}\n"; - + char const* interface = R"([ { "name": "c", @@ -203,8 +205,8 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) ] } ])"; - - BOOST_CHECK(checkInterface(sourceCode, interface)); + + checkInterface(sourceCode, interface); } BOOST_AUTO_TEST_SUITE_END() From 88b1bb25404bba285f903668ae0d3c44642d9a92 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 2 Dec 2014 10:41:18 +0100 Subject: [PATCH 03/26] More Natspec JSON export tests and better error reporting --- libsolidity/CompilerStack.cpp | 2 +- test/solidityJSONInterfaceTest.cpp | 17 ++++++- test/solidityNatspecJSON.cpp | 76 +++++++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index e25438f7c..77a019b5f 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -134,7 +134,7 @@ string const& CompilerStack::getDocumentation() if (m_documentation.empty()) { Json::Value doc; - Json::Value methods; + Json::Value methods(Json::objectValue); vector exportedFunctions = m_contractASTNode->getInterfaceFunctions(); for (FunctionDefinition const* f: exportedFunctions) diff --git a/test/solidityJSONInterfaceTest.cpp b/test/solidityJSONInterfaceTest.cpp index b91880130..f46a3ad36 100644 --- a/test/solidityJSONInterfaceTest.cpp +++ b/test/solidityJSONInterfaceTest.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace dev { @@ -36,7 +37,19 @@ class InterfaceChecker public: void checkInterface(std::string const& _code, std::string const& _expectedInterfaceString) { - m_compilerStack.parse(_code); + try + { + m_compilerStack.parse(_code); + } + catch (const std::exception& e) + { + std::string const* extra = boost::get_error_info(e); + std::string msg = std::string("Parsing contract failed with: ") + + e.what() + std::string("\n"); + if (extra) + msg += *extra; + BOOST_FAIL(msg); + } std::string generatedInterfaceString = m_compilerStack.getInterface(); Json::Value generatedInterface; m_reader.parse(generatedInterfaceString, generatedInterface); @@ -52,7 +65,7 @@ private: Json::Reader m_reader; }; -BOOST_FIXTURE_TEST_SUITE(solidityABIJSON, InterfaceChecker) +BOOST_FIXTURE_TEST_SUITE(SolidityABIJSON, InterfaceChecker) BOOST_AUTO_TEST_CASE(basic_test) { diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp index 5918eec99..5a4649d2e 100644 --- a/test/solidityNatspecJSON.cpp +++ b/test/solidityNatspecJSON.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace dev { @@ -36,7 +37,19 @@ class DocumentationChecker public: void checkNatspec(std::string const& _code, std::string const& _expectedDocumentationString) { - m_compilerStack.parse(_code); + try + { + m_compilerStack.parse(_code); + } + catch (const std::exception& e) + { + std::string const* extra = boost::get_error_info(e); + std::string msg = std::string("Parsing contract failed with: ") + + e.what() + std::string("\n"); + if (extra) + msg += *extra; + BOOST_FAIL(msg); + } auto generatedDocumentationString = m_compilerStack.getDocumentation(); Json::Value generatedDocumentation; m_reader.parse(generatedDocumentationString, generatedDocumentation); @@ -69,6 +82,67 @@ BOOST_AUTO_TEST_CASE(basic_test) checkNatspec(sourceCode, natspec); } +BOOST_AUTO_TEST_CASE(multiline_comment) +{ + char const* sourceCode = "contract test {\n" + " /// Multiplies `a` by 7\n" + " /// and then adds `b`\n" + " function mul_and_add(uint a, uint256 b) returns(uint256 d)\n" + " {\n" + " return (a * 7) + b;\n" + " }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul_and_add\":{ \"user\": \" Multiplies `a` by 7\n and then adds `b`\"}" + "}}"; + + checkNatspec(sourceCode, natspec); +} + +BOOST_AUTO_TEST_CASE(multiple_functions) +{ + char const* sourceCode = "contract test {\n" + " /// Multiplies `a` by 7\n" + " /// and then adds `b`\n" + " function mul_and_add(uint a, uint256 b) returns(uint256 d)\n" + " {\n" + " return (a * 7) + b;\n" + " }\n" + "\n" + " /// Divides `input` by `div`\n" + " function divide(uint input, uint div) returns(uint d)\n" + " {\n" + " return input / div;\n" + " }\n" + " /// Subtracts 3 from `input`\n" + " function sub(int input) returns(int d)\n" + " {\n" + " return input - 3;\n" + " }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul_and_add\":{ \"user\": \" Multiplies `a` by 7\n and then adds `b`\"}," + " \"divide\":{ \"user\": \" Divides `input` by `div`\"}," + " \"sub\":{ \"user\": \" Subtracts 3 from `input`\"}" + "}}"; + + checkNatspec(sourceCode, natspec); +} + +BOOST_AUTO_TEST_CASE(empty_contract) +{ + char const* sourceCode = "contract test {\n" + "}\n"; + + char const* natspec = "{\"methods\":{} }"; + + checkNatspec(sourceCode, natspec); +} + BOOST_AUTO_TEST_SUITE_END() From 93c488ce736f9f26edb556a4e112b188bdf4465e Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 2 Dec 2014 11:03:34 +0100 Subject: [PATCH 04/26] Handle absence of Natspec doc and add option to solc --- libsolidity/CompilerStack.cpp | 8 ++++++-- solc/main.cpp | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 77a019b5f..2b2991584 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -140,8 +140,12 @@ string const& CompilerStack::getDocumentation() for (FunctionDefinition const* f: exportedFunctions) { Json::Value user; - user["user"] = Json::Value(*f->getDocumentation()); - methods[f->getName()] = user; + auto strPtr = f->getDocumentation(); + if (strPtr) + { + user["user"] = Json::Value(*strPtr); + methods[f->getName()] = user; + } } doc["methods"] = methods; m_documentation = writer.write(doc); diff --git a/solc/main.cpp b/solc/main.cpp index a7216e594..6ca130a79 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -136,6 +136,7 @@ int main(int argc, char** argv) cout << eth::disassemble(compiler.getBytecode()) << endl; cout << "Binary: " << toHex(compiler.getBytecode()) << endl; cout << "Interface specification: " << compiler.getInterface() << endl; + cout << "Natspec documentation: " << compiler.getDocumentation() << endl; return 0; } From c89fc7df63fef3e430e17550e28083d71e3db647 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 2 Dec 2014 12:14:24 +0100 Subject: [PATCH 05/26] Removing unneeded local variable in CompilerStack::getDocumentation() --- libsolidity/CompilerStack.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 2b2991584..48a37b9cf 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -136,8 +136,7 @@ string const& CompilerStack::getDocumentation() Json::Value doc; Json::Value methods(Json::objectValue); - vector exportedFunctions = m_contractASTNode->getInterfaceFunctions(); - for (FunctionDefinition const* f: exportedFunctions) + for (FunctionDefinition const* f: m_contractASTNode->getInterfaceFunctions()) { Json::Value user; auto strPtr = f->getDocumentation(); From 9ff245ab52bd35cf7fa5ad845df9cdd7bd693f7c Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 2 Dec 2014 17:18:09 +0100 Subject: [PATCH 06/26] Simplifying lambda function in CompilerStack::getInterface() --- libsolidity/CompilerStack.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 48a37b9cf..e44a10fbe 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -100,24 +100,22 @@ string const& CompilerStack::getInterface() Json::Value inputs(Json::arrayValue); Json::Value outputs(Json::arrayValue); - auto streamVariables = [&](vector> const& _vars, - Json::Value &json) + auto streamVariables = [](vector> const& _vars) { + Json::Value params(Json::arrayValue); for (ASTPointer const& var: _vars) { Json::Value input; input["name"] = var->getName(); input["type"] = var->getType()->toString(); - json.append(input); + params.append(input); } + return params; }; method["name"] = f->getName(); - streamVariables(f->getParameters(), inputs); - method["inputs"] = inputs; - streamVariables(f->getReturnParameters(), outputs); - method["outputs"] = outputs; - + method["inputs"] = streamVariables(f->getParameters()); + method["outputs"] = streamVariables(f->getReturnParameters()); methods.append(method); } m_interface = writer.write(methods); From 994e891078c0197d6c6b0347d4040f08f6564958 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 3 Dec 2014 13:50:04 +0100 Subject: [PATCH 07/26] Separate user and dev natspec documentation - plus other small changes according to the spec --- libsolidity/CompilerStack.cpp | 23 +++++++++++++++++----- libsolidity/CompilerStack.h | 10 +++++++--- solc/main.cpp | 2 +- test/solidityNatspecJSON.cpp | 37 +++++++++++++++++++++-------------- 4 files changed, 48 insertions(+), 24 deletions(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index e44a10fbe..b12097052 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -123,13 +123,13 @@ string const& CompilerStack::getInterface() return m_interface; } -string const& CompilerStack::getDocumentation() +string const& CompilerStack::getUserDocumentation() { Json::StyledWriter writer; if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - if (m_documentation.empty()) + if (m_userDocumentation.empty()) { Json::Value doc; Json::Value methods(Json::objectValue); @@ -140,14 +140,27 @@ string const& CompilerStack::getDocumentation() auto strPtr = f->getDocumentation(); if (strPtr) { - user["user"] = Json::Value(*strPtr); + user["notice"] = Json::Value(*strPtr); methods[f->getName()] = user; } } doc["methods"] = methods; - m_documentation = writer.write(doc); + m_userDocumentation = writer.write(doc); } - return m_documentation; + return m_userDocumentation; +} + +string const& CompilerStack::getDevDocumentation() +{ + Json::StyledWriter writer; + if (!m_parseSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + + if (m_devDocumentation.empty()) + { + // TODO + } + return m_devDocumentation; } bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 74784c5ef..4e0d2251b 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -62,9 +62,12 @@ public: /// Returns a string representing the contract interface in JSON. /// Prerequisite: Successful call to parse or compile. std::string const& getInterface(); - /// Returns a string representing the contract documentation in JSON. + /// Returns a string representing the contract's user documentation in JSON. /// Prerequisite: Successful call to parse or compile. - std::string const& getDocumentation(); + std::string const& getUserDocumentation(); + /// Returns a string representing the contract's developer documentation in JSON. + /// Prerequisite: Successful call to parse or compile. + std::string const& getDevDocumentation(); /// Returns the previously used scanner, useful for counting lines during error reporting. Scanner const& getScanner() const { return *m_scanner; } @@ -80,7 +83,8 @@ private: std::shared_ptr m_contractASTNode; bool m_parseSuccessful; std::string m_interface; - std::string m_documentation; + std::string m_userDocumentation; + std::string m_devDocumentation; std::shared_ptr m_compiler; bytes m_bytecode; }; diff --git a/solc/main.cpp b/solc/main.cpp index 6ca130a79..29239a717 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -136,7 +136,7 @@ int main(int argc, char** argv) cout << eth::disassemble(compiler.getBytecode()) << endl; cout << "Binary: " << toHex(compiler.getBytecode()) << endl; cout << "Interface specification: " << compiler.getInterface() << endl; - cout << "Natspec documentation: " << compiler.getDocumentation() << endl; + cout << "Natspec user documentation: " << compiler.getUserDocumentation() << endl; return 0; } diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp index 5a4649d2e..f729e4ff8 100644 --- a/test/solidityNatspecJSON.cpp +++ b/test/solidityNatspecJSON.cpp @@ -35,8 +35,11 @@ namespace test class DocumentationChecker { public: - void checkNatspec(std::string const& _code, std::string const& _expectedDocumentationString) + void checkNatspec(std::string const& _code, + std::string const& _expectedDocumentationString, + bool _userDocumentation) { + std::string generatedDocumentationString; try { m_compilerStack.parse(_code); @@ -50,7 +53,11 @@ public: msg += *extra; BOOST_FAIL(msg); } - auto generatedDocumentationString = m_compilerStack.getDocumentation(); + + if (_userDocumentation) + generatedDocumentationString = m_compilerStack.getUserDocumentation(); + else + generatedDocumentationString = m_compilerStack.getDevDocumentation(); Json::Value generatedDocumentation; m_reader.parse(generatedDocumentationString, generatedDocumentation); Json::Value expectedDocumentation; @@ -67,7 +74,7 @@ private: BOOST_FIXTURE_TEST_SUITE(SolidityNatspecJSON, DocumentationChecker) -BOOST_AUTO_TEST_CASE(basic_test) +BOOST_AUTO_TEST_CASE(user_basic_test) { char const* sourceCode = "contract test {\n" " /// Multiplies `a` by 7\n" @@ -76,13 +83,13 @@ BOOST_AUTO_TEST_CASE(basic_test) char const* natspec = "{" "\"methods\":{" - " \"mul\":{ \"user\": \" Multiplies `a` by 7\"}" + " \"mul\":{ \"notice\": \" Multiplies `a` by 7\"}" "}}"; - checkNatspec(sourceCode, natspec); + checkNatspec(sourceCode, natspec, true); } -BOOST_AUTO_TEST_CASE(multiline_comment) +BOOST_AUTO_TEST_CASE(user_multiline_comment) { char const* sourceCode = "contract test {\n" " /// Multiplies `a` by 7\n" @@ -95,13 +102,13 @@ BOOST_AUTO_TEST_CASE(multiline_comment) char const* natspec = "{" "\"methods\":{" - " \"mul_and_add\":{ \"user\": \" Multiplies `a` by 7\n and then adds `b`\"}" + " \"mul_and_add\":{ \"notice\": \" Multiplies `a` by 7\n and then adds `b`\"}" "}}"; - checkNatspec(sourceCode, natspec); + checkNatspec(sourceCode, natspec, true); } -BOOST_AUTO_TEST_CASE(multiple_functions) +BOOST_AUTO_TEST_CASE(user_multiple_functions) { char const* sourceCode = "contract test {\n" " /// Multiplies `a` by 7\n" @@ -125,22 +132,22 @@ BOOST_AUTO_TEST_CASE(multiple_functions) char const* natspec = "{" "\"methods\":{" - " \"mul_and_add\":{ \"user\": \" Multiplies `a` by 7\n and then adds `b`\"}," - " \"divide\":{ \"user\": \" Divides `input` by `div`\"}," - " \"sub\":{ \"user\": \" Subtracts 3 from `input`\"}" + " \"mul_and_add\":{ \"notice\": \" Multiplies `a` by 7\n and then adds `b`\"}," + " \"divide\":{ \"notice\": \" Divides `input` by `div`\"}," + " \"sub\":{ \"notice\": \" Subtracts 3 from `input`\"}" "}}"; - checkNatspec(sourceCode, natspec); + checkNatspec(sourceCode, natspec, true); } -BOOST_AUTO_TEST_CASE(empty_contract) +BOOST_AUTO_TEST_CASE(user_empty_contract) { char const* sourceCode = "contract test {\n" "}\n"; char const* natspec = "{\"methods\":{} }"; - checkNatspec(sourceCode, natspec); + checkNatspec(sourceCode, natspec, true); } From 4613214098850490c0da24cfca5c36e5ce014512 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 3 Dec 2014 16:40:37 +0100 Subject: [PATCH 08/26] Moving all Interface and Documentation functionality to own class - Creating the Interface Handler class which will take care of the parsing of Natspec comments and of interfacing with and outputing to JSON files. - Will also handle the ABI interface creation --- libsolidity/CompilerStack.cpp | 90 +++++++----------------------- libsolidity/CompilerStack.h | 26 ++++++--- libsolidity/InterfaceHandler.cpp | 88 +++++++++++++++++++++++++++++ libsolidity/InterfaceHandler.h | 74 ++++++++++++++++++++++++ solc/main.cpp | 5 +- test/solidityJSONInterfaceTest.cpp | 2 +- test/solidityNatspecJSON.cpp | 4 +- 7 files changed, 205 insertions(+), 84 deletions(-) create mode 100644 libsolidity/InterfaceHandler.cpp create mode 100644 libsolidity/InterfaceHandler.h diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index b12097052..6f80d245c 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -27,8 +27,7 @@ #include #include #include - -#include +#include using namespace std; @@ -37,6 +36,8 @@ namespace dev namespace solidity { +CompilerStack::CompilerStack():m_interfaceHandler(make_shared()){} + void CompilerStack::setSource(string const& _sourceCode) { reset(); @@ -83,84 +84,33 @@ void CompilerStack::streamAssembly(ostream& _outStream) m_compiler->streamAssembly(_outStream); } -string const& CompilerStack::getInterface() -{ - Json::StyledWriter writer; - if (!m_parseSuccessful) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - - if (m_interface.empty()) - { - Json::Value methods(Json::arrayValue); - - vector exportedFunctions = m_contractASTNode->getInterfaceFunctions(); - for (FunctionDefinition const* f: exportedFunctions) - { - Json::Value method; - Json::Value inputs(Json::arrayValue); - Json::Value outputs(Json::arrayValue); - - auto streamVariables = [](vector> const& _vars) - { - Json::Value params(Json::arrayValue); - for (ASTPointer const& var: _vars) - { - Json::Value input; - input["name"] = var->getName(); - input["type"] = var->getType()->toString(); - params.append(input); - } - return params; - }; - - method["name"] = f->getName(); - method["inputs"] = streamVariables(f->getParameters()); - method["outputs"] = streamVariables(f->getReturnParameters()); - methods.append(method); - } - m_interface = writer.write(methods); - } - return m_interface; -} - -string const& CompilerStack::getUserDocumentation() +std::string const* CompilerStack::getJsonDocumentation(enum documentation_type _type) { - Json::StyledWriter writer; if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - if (m_userDocumentation.empty()) + auto createOrReturnDoc = [&, this](std::unique_ptr& _doc) { - Json::Value doc; - Json::Value methods(Json::objectValue); - - for (FunctionDefinition const* f: m_contractASTNode->getInterfaceFunctions()) + if(!_doc) { - Json::Value user; - auto strPtr = f->getDocumentation(); - if (strPtr) - { - user["notice"] = Json::Value(*strPtr); - methods[f->getName()] = user; - } + _doc = m_interfaceHandler->getDocumentation(m_contractASTNode, _type); } - doc["methods"] = methods; - m_userDocumentation = writer.write(doc); - } - return m_userDocumentation; -} - -string const& CompilerStack::getDevDocumentation() -{ - Json::StyledWriter writer; - if (!m_parseSuccessful) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + }; - if (m_devDocumentation.empty()) + switch (_type) { - // TODO + case NATSPEC_USER: + createOrReturnDoc(m_userDocumentation); + return m_userDocumentation.get(); + case NATSPEC_DEV: + createOrReturnDoc(m_devDocumentation); + return m_devDocumentation.get(); + case ABI_INTERFACE: + createOrReturnDoc(m_interface); + return m_interface.get(); } - return m_devDocumentation; + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Internal error")); + return nullptr; } bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 4e0d2251b..7dc86e2be 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -35,6 +35,14 @@ class Scanner; class ContractDefinition; class Compiler; class GlobalContext; +class InterfaceHandler; + +enum documentation_type : unsigned short +{ + NATSPEC_USER = 1, + NATSPEC_DEV, + ABI_INTERFACE +}; /** * Easy to use and self-contained Solidity compiler with as few header dependencies as possible. @@ -44,7 +52,7 @@ class GlobalContext; class CompilerStack { public: - CompilerStack() {} + CompilerStack(); void reset() { *this = CompilerStack(); } void setSource(std::string const& _sourceCode); void parse(); @@ -62,12 +70,11 @@ public: /// Returns a string representing the contract interface in JSON. /// Prerequisite: Successful call to parse or compile. std::string const& getInterface(); - /// Returns a string representing the contract's user documentation in JSON. - /// Prerequisite: Successful call to parse or compile. - std::string const& getUserDocumentation(); - /// Returns a string representing the contract's developer documentation in JSON. + /// Returns a string representing the contract's documentation in JSON. /// Prerequisite: Successful call to parse or compile. - std::string const& getDevDocumentation(); + /// @param type The type of the documentation to get. + /// Can be one of 3 types defined at @c documentation_type + std::string const* getJsonDocumentation(enum documentation_type type); /// Returns the previously used scanner, useful for counting lines during error reporting. Scanner const& getScanner() const { return *m_scanner; } @@ -82,10 +89,11 @@ private: std::shared_ptr m_globalContext; std::shared_ptr m_contractASTNode; bool m_parseSuccessful; - std::string m_interface; - std::string m_userDocumentation; - std::string m_devDocumentation; + std::unique_ptr m_interface; + std::unique_ptr m_userDocumentation; + std::unique_ptr m_devDocumentation; std::shared_ptr m_compiler; + std::shared_ptr m_interfaceHandler; bytes m_bytecode; }; diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp new file mode 100644 index 000000000..c0e07280e --- /dev/null +++ b/libsolidity/InterfaceHandler.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace dev { +namespace solidity { + +InterfaceHandler::InterfaceHandler() +{ +} + +std::unique_ptr InterfaceHandler::getDocumentation(std::shared_ptr _contractDef, + enum documentation_type _type) +{ + switch(_type) + { + case NATSPEC_USER: + return getUserDocumentation(_contractDef); + case NATSPEC_DEV: + return getDevDocumentation(_contractDef); + case ABI_INTERFACE: + return getABIInterface(_contractDef); + } + + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Internal error")); + return nullptr; +} + +std::unique_ptr InterfaceHandler::getABIInterface(std::shared_ptr _contractDef) +{ + Json::Value methods(Json::arrayValue); + + std::vector exportedFunctions = _contractDef->getInterfaceFunctions(); + for (FunctionDefinition const* f: exportedFunctions) + { + Json::Value method; + Json::Value inputs(Json::arrayValue); + Json::Value outputs(Json::arrayValue); + + auto streamVariables = [](std::vector> const& _vars) + { + Json::Value params(Json::arrayValue); + for (ASTPointer const& var: _vars) + { + Json::Value input; + input["name"] = var->getName(); + input["type"] = var->getType()->toString(); + params.append(input); + } + return params; + }; + + method["name"] = f->getName(); + method["inputs"] = streamVariables(f->getParameters()); + method["outputs"] = streamVariables(f->getReturnParameters()); + methods.append(method); + } + return std::unique_ptr(new std::string(m_writer.write(methods))); +} + +std::unique_ptr InterfaceHandler::getUserDocumentation(std::shared_ptr _contractDef) +{ + Json::Value doc; + Json::Value methods(Json::objectValue); + + for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) + { + Json::Value user; + auto strPtr = f->getDocumentation(); + if (strPtr) + { + user["notice"] = Json::Value(*strPtr); + methods[f->getName()] = user; + } + } + doc["methods"] = methods; + + return std::unique_ptr(new std::string(m_writer.write(doc))); +} + +std::unique_ptr InterfaceHandler::getDevDocumentation(std::shared_ptr _contractDef) +{ + //TODO + return nullptr; +} + +} //solidity NS +} // dev NS diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h new file mode 100644 index 000000000..ed6c8ba44 --- /dev/null +++ b/libsolidity/InterfaceHandler.h @@ -0,0 +1,74 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Lefteris + * @date 2014 + * Takes the parsed AST and produces the Natspec + * documentation and the ABI interface + * https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format + * + * Can generally deal with JSON files + */ + +#pragma once + +#include +#include +#include + +namespace dev { +namespace solidity { + +// Forward declarations +class ContractDefinition; +enum documentation_type: unsigned short; + +class InterfaceHandler +{ +public: + InterfaceHandler(); + + /// Get the given type of documentation + /// @param _contractDef The contract definition + /// @param _type The type of the documentation. Can be one of the + /// types provided by @c documentation_type + /// @return A unique pointer contained string with the json + /// representation of provided type + std::unique_ptr getDocumentation(std::shared_ptr _contractDef, + enum documentation_type _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(std::shared_ptr _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(std::shared_ptr _contractDef); + /// Get 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(std::shared_ptr _contractDef); + +private: + Json::StyledWriter m_writer; +}; + +} //solidity NS +} // dev NS diff --git a/solc/main.cpp b/solc/main.cpp index 29239a717..daeb2707c 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -135,8 +135,9 @@ int main(int argc, char** argv) cout << "Opcodes:" << endl; cout << eth::disassemble(compiler.getBytecode()) << endl; cout << "Binary: " << toHex(compiler.getBytecode()) << endl; - cout << "Interface specification: " << compiler.getInterface() << endl; - cout << "Natspec user documentation: " << compiler.getUserDocumentation() << endl; + cout << "Interface specification: " << compiler.getJsonDocumentation(ABI_INTERFACE) << endl; + cout << "Natspec user documentation: " << compiler.getJsonDocumentation(NATSPEC_USER) << endl; + cout << "Natspec developer documentation: " << compiler.getJsonDocumentation(NATSPEC_DEV) << endl; return 0; } diff --git a/test/solidityJSONInterfaceTest.cpp b/test/solidityJSONInterfaceTest.cpp index f46a3ad36..8fe0ea653 100644 --- a/test/solidityJSONInterfaceTest.cpp +++ b/test/solidityJSONInterfaceTest.cpp @@ -50,7 +50,7 @@ public: msg += *extra; BOOST_FAIL(msg); } - std::string generatedInterfaceString = m_compilerStack.getInterface(); + std::string generatedInterfaceString = *m_compilerStack.getJsonDocumentation(ABI_INTERFACE); Json::Value generatedInterface; m_reader.parse(generatedInterfaceString, generatedInterface); Json::Value expectedInterface; diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp index f729e4ff8..9e24d23ed 100644 --- a/test/solidityNatspecJSON.cpp +++ b/test/solidityNatspecJSON.cpp @@ -55,9 +55,9 @@ public: } if (_userDocumentation) - generatedDocumentationString = m_compilerStack.getUserDocumentation(); + generatedDocumentationString = *m_compilerStack.getJsonDocumentation(NATSPEC_USER); else - generatedDocumentationString = m_compilerStack.getDevDocumentation(); + generatedDocumentationString = *m_compilerStack.getJsonDocumentation(NATSPEC_DEV); Json::Value generatedDocumentation; m_reader.parse(generatedDocumentationString, generatedDocumentation); Json::Value expectedDocumentation; From eeb186c834aaf30ba63a10585cc9c66ae33fcd6c Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 3 Dec 2014 17:46:04 +0100 Subject: [PATCH 09/26] Work in progress for parsing natspec doxytags --- libsolidity/InterfaceHandler.cpp | 69 ++++++++++++++++++++++++++++++-- libsolidity/InterfaceHandler.h | 11 ++++- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index c0e07280e..a2c52c1f0 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -5,12 +5,14 @@ namespace dev { namespace solidity { +/* -- public -- */ + InterfaceHandler::InterfaceHandler() { } std::unique_ptr InterfaceHandler::getDocumentation(std::shared_ptr _contractDef, - enum documentation_type _type) + enum documentation_type _type) { switch(_type) { @@ -80,8 +82,69 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(std::shared_ std::unique_ptr InterfaceHandler::getDevDocumentation(std::shared_ptr _contractDef) { - //TODO - return nullptr; + Json::Value doc; + Json::Value methods(Json::objectValue); + + for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) + { + Json::Value method; + auto strPtr = f->getDocumentation(); + if (strPtr) + { + m_dev.clear(); + parseDocString(*strPtr); + + method["dev"] = Json::Value(m_dev); + methods[f->getName()] = method; + } + } + doc["methods"] = methods; + + return std::unique_ptr(new std::string(m_writer.write(doc))); +} + +/* -- private -- */ +size_t InterfaceHandler::parseDocTag(std::string const& _string, std::string const& _tag, size_t _pos) +{ + size_t nlPos = _pos; + if (_tag == "dev") + { + nlPos = _string.find("\n", _pos); + m_dev += _string.substr(_pos, + nlPos == std::string::npos ? + _string.length() : + nlPos - _pos); + } + else if (_tag == "notice") + { + nlPos = _string.find("\n", _pos); + m_notice += _string.substr(_pos, + nlPos == std::string::npos ? + _string.length() : + nlPos - _pos); + } + else + { + //TODO: Some form of warning + } + + return nlPos; +} + +void InterfaceHandler::parseDocString(std::string const& _string, size_t _startPos) +{ + size_t pos2; + size_t pos1 = _string.find("@", _startPos); + + if (pos1 == std::string::npos) + return; // no doxytags found + + pos2 = _string.find(" ", pos1); + if (pos2 == std::string::npos) + return; //no end of tag found + + size_t newPos = parseDocTag(_string, _string.substr(pos1 + 1, pos2 - pos1), pos2); + parseDocString(_string, newPos); } } //solidity NS diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index ed6c8ba44..125ecda4b 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/InterfaceHandler.h @@ -20,7 +20,7 @@ * Takes the parsed AST and produces the Natspec * documentation and the ABI interface * https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format - * + * * Can generally deal with JSON files */ @@ -44,7 +44,7 @@ public: /// Get the given type of documentation /// @param _contractDef The contract definition - /// @param _type The type of the documentation. Can be one of the + /// @param _type The type of the documentation. Can be one of the /// types provided by @c documentation_type /// @return A unique pointer contained string with the json /// representation of provided type @@ -67,7 +67,14 @@ public: std::unique_ptr getDevDocumentation(std::shared_ptr _contractDef); private: + void parseDocString(std::string const& _string, size_t _startPos = 0); + size_t parseDocTag(std::string const& _string, std::string const& _tag, size_t _pos); + Json::StyledWriter m_writer; + + // internal state + std::string m_notice; + std::string m_dev; }; } //solidity NS From 2f16217574b2d610967ee094e209065d50531f90 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 4 Dec 2014 01:27:38 +0100 Subject: [PATCH 10/26] Styling in libsolidity's InterfaceHandler --- libsolidity/CompilerStack.cpp | 4 ++-- libsolidity/CompilerStack.h | 4 ++-- libsolidity/InterfaceHandler.cpp | 8 ++++---- libsolidity/InterfaceHandler.h | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 6f80d245c..3e9791bd2 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -84,12 +84,12 @@ void CompilerStack::streamAssembly(ostream& _outStream) m_compiler->streamAssembly(_outStream); } -std::string const* CompilerStack::getJsonDocumentation(enum documentation_type _type) +std::string const* CompilerStack::getJsonDocumentation(enum documentationType _type) { if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - auto createOrReturnDoc = [&, this](std::unique_ptr& _doc) + auto createOrReturnDoc = [this, _type](std::unique_ptr& _doc) { if(!_doc) { diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 7dc86e2be..511951291 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -37,7 +37,7 @@ class Compiler; class GlobalContext; class InterfaceHandler; -enum documentation_type : unsigned short +enum documentationType: unsigned short { NATSPEC_USER = 1, NATSPEC_DEV, @@ -74,7 +74,7 @@ public: /// Prerequisite: Successful call to parse or compile. /// @param type The type of the documentation to get. /// Can be one of 3 types defined at @c documentation_type - std::string const* getJsonDocumentation(enum documentation_type type); + std::string const* getJsonDocumentation(enum documentationType type); /// Returns the previously used scanner, useful for counting lines during error reporting. Scanner const& getScanner() const { return *m_scanner; } diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index a2c52c1f0..61570b8d7 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -12,7 +12,7 @@ InterfaceHandler::InterfaceHandler() } std::unique_ptr InterfaceHandler::getDocumentation(std::shared_ptr _contractDef, - enum documentation_type _type) + enum documentationType _type) { switch(_type) { @@ -39,7 +39,7 @@ std::unique_ptr InterfaceHandler::getABIInterface(std::shared_ptr> const& _vars) + auto populateParameters = [](std::vector> const& _vars) { Json::Value params(Json::arrayValue); for (ASTPointer const& var: _vars) @@ -53,8 +53,8 @@ std::unique_ptr InterfaceHandler::getABIInterface(std::shared_ptrgetName(); - method["inputs"] = streamVariables(f->getParameters()); - method["outputs"] = streamVariables(f->getReturnParameters()); + method["inputs"] = populateParameters(f->getParameters()); + method["outputs"] = populateParameters(f->getReturnParameters()); methods.append(method); } return std::unique_ptr(new std::string(m_writer.write(methods))); diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index 125ecda4b..5c8bf5bca 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/InterfaceHandler.h @@ -35,7 +35,7 @@ namespace solidity { // Forward declarations class ContractDefinition; -enum documentation_type: unsigned short; +enum documentationType: unsigned short; class InterfaceHandler { @@ -49,7 +49,7 @@ public: /// @return A unique pointer contained string with the json /// representation of provided type std::unique_ptr getDocumentation(std::shared_ptr _contractDef, - enum documentation_type _type); + enum documentationType _type); /// Get the ABI Interface of the contract /// @param _contractDef The contract definition /// @return A unique pointer contained string with the json From e560ef983afde181027fc52f49f8c3b15843a164 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Thu, 4 Dec 2014 01:36:48 +0100 Subject: [PATCH 11/26] fixed mix project not being built on macos --- mix/CodeEditorExtensionManager.cpp | 1 - mix/EthereumMacOSXBundleInfo.plist.in | 38 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 mix/EthereumMacOSXBundleInfo.plist.in diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp index 596aea165..c778d466f 100644 --- a/mix/CodeEditorExtensionManager.cpp +++ b/mix/CodeEditorExtensionManager.cpp @@ -27,7 +27,6 @@ #include #include #include "ConstantCompilationCtrl.h" -#include "features.h" #include "ApplicationCtx.h" #include "CodeEditorExtensionManager.h" using namespace dev::mix; diff --git a/mix/EthereumMacOSXBundleInfo.plist.in b/mix/EthereumMacOSXBundleInfo.plist.in new file mode 100644 index 000000000..684ad7908 --- /dev/null +++ b/mix/EthereumMacOSXBundleInfo.plist.in @@ -0,0 +1,38 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + LSRequiresCarbon + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + NSHighResolutionCapable + + + From 5fe11335834395d5b8033a5b24eb6ae7c225e4f2 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 4 Dec 2014 09:42:38 +0100 Subject: [PATCH 12/26] Parsing notice and dev doxytags. - Only initial work done. Still need to refine the logic and incorporate all the other types of tags. - Added/Modified some tests - Work in progress --- libsolidity/InterfaceHandler.cpp | 90 ++++++++++++++++++++++++-------- libsolidity/InterfaceHandler.h | 8 +++ test/solidityNatspecJSON.cpp | 58 ++++++++++++++++---- 3 files changed, 122 insertions(+), 34 deletions(-) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 61570b8d7..e7da5e6d5 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -9,6 +9,7 @@ namespace solidity { InterfaceHandler::InterfaceHandler() { + m_lastTag = DOCTAG_NONE; } std::unique_ptr InterfaceHandler::getDocumentation(std::shared_ptr _contractDef, @@ -71,7 +72,9 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(std::shared_ auto strPtr = f->getDocumentation(); if (strPtr) { - user["notice"] = Json::Value(*strPtr); + m_notice.clear(); + parseDocString(*strPtr); + user["notice"] = Json::Value(m_notice); methods[f->getName()] = user; } } @@ -94,7 +97,7 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(std::shared_p m_dev.clear(); parseDocString(*strPtr); - method["dev"] = Json::Value(m_dev); + method["details"] = Json::Value(m_dev); methods[f->getName()] = method; } } @@ -106,26 +109,55 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(std::shared_p /* -- private -- */ size_t InterfaceHandler::parseDocTag(std::string const& _string, std::string const& _tag, size_t _pos) { + //TODO: This is pretty naive at the moment. e.g. need to check for + // '@' between _pos and \n, remove redundancy e.t.c. size_t nlPos = _pos; - if (_tag == "dev") - { - nlPos = _string.find("\n", _pos); - m_dev += _string.substr(_pos, - nlPos == std::string::npos ? - _string.length() : - nlPos - _pos); - } - else if (_tag == "notice") + if (m_lastTag == DOCTAG_NONE || _tag != "") { - nlPos = _string.find("\n", _pos); - m_notice += _string.substr(_pos, - nlPos == std::string::npos ? - _string.length() : - nlPos - _pos); + if (_tag == "dev") + { + nlPos = _string.find("\n", _pos); + m_dev += _string.substr(_pos, + nlPos == std::string::npos ? + _string.length() : + nlPos - _pos); + m_lastTag = DOCTAG_DEV; + } + else if (_tag == "notice") + { + nlPos = _string.find("\n", _pos); + m_notice += _string.substr(_pos, + nlPos == std::string::npos ? + _string.length() : + nlPos - _pos); + m_lastTag = DOCTAG_NOTICE; + } + else + { + //TODO: Some form of warning + } } else { - //TODO: Some form of warning + switch(m_lastTag) + { + case DOCTAG_DEV: + nlPos = _string.find("\n", _pos); + m_dev += _string.substr(_pos, + nlPos == std::string::npos ? + _string.length() : + nlPos - _pos); + break; + case DOCTAG_NOTICE: + nlPos = _string.find("\n", _pos); + m_notice += _string.substr(_pos, + nlPos == std::string::npos ? + _string.length() : + nlPos - _pos); + break; + default: + break; + } } return nlPos; @@ -134,16 +166,28 @@ size_t InterfaceHandler::parseDocTag(std::string const& _string, std::string con void InterfaceHandler::parseDocString(std::string const& _string, size_t _startPos) { size_t pos2; + size_t newPos = _startPos; size_t pos1 = _string.find("@", _startPos); - if (pos1 == std::string::npos) - return; // no doxytags found + if (pos1 != std::string::npos) + { + // we found a tag + pos2 = _string.find(" ", pos1); + if (pos2 == std::string::npos) + { + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("End of tag not found")); + return; //no end of tag found + } - pos2 = _string.find(" ", pos1); - if (pos2 == std::string::npos) - return; //no end of tag found + newPos = parseDocTag(_string, _string.substr(pos1 + 1, pos2 - pos1 - 1), pos2 + 1); + } + else + { + newPos = parseDocTag(_string, "", _startPos + 1); + } - size_t newPos = parseDocTag(_string, _string.substr(pos1 + 1, pos2 - pos1), pos2); + if (newPos == std::string::npos) + return; // EOS parseDocString(_string, newPos); } diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index 5c8bf5bca..6f2f2937d 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/InterfaceHandler.h @@ -37,6 +37,13 @@ namespace solidity { class ContractDefinition; enum documentationType: unsigned short; +enum docTagType +{ + DOCTAG_NONE = 0, + DOCTAG_DEV, + DOCTAG_NOTICE, +}; + class InterfaceHandler { public: @@ -73,6 +80,7 @@ private: Json::StyledWriter m_writer; // internal state + enum docTagType m_lastTag; std::string m_notice; std::string m_dev; }; diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp index 9e24d23ed..d74aa8ab9 100644 --- a/test/solidityNatspecJSON.cpp +++ b/test/solidityNatspecJSON.cpp @@ -77,22 +77,59 @@ BOOST_FIXTURE_TEST_SUITE(SolidityNatspecJSON, DocumentationChecker) BOOST_AUTO_TEST_CASE(user_basic_test) { char const* sourceCode = "contract test {\n" - " /// Multiplies `a` by 7\n" + " /// @notice Multiplies `a` by 7\n" " function mul(uint a) returns(uint d) { return a * 7; }\n" "}\n"; char const* natspec = "{" "\"methods\":{" - " \"mul\":{ \"notice\": \" Multiplies `a` by 7\"}" + " \"mul\":{ \"notice\": \"Multiplies `a` by 7\"}" "}}"; checkNatspec(sourceCode, natspec, true); } +BOOST_AUTO_TEST_CASE(dev_basic_test) +{ + char const* sourceCode = "contract test {\n" + " /// @dev Multiplies a number by 7\n" + " function mul(uint a) returns(uint d) { return a * 7; }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul\":{ \"details\": \"Multiplies a number by 7\"}" + "}}"; + + checkNatspec(sourceCode, natspec, false); +} + +BOOST_AUTO_TEST_CASE(dev_and_user_basic_test) +{ + char const* sourceCode = "contract test {\n" + " /// @notice Multiplies `a` by 7\n" + " /// @dev Multiplies a number by 7\n" + " function mul(uint a) returns(uint d) { return a * 7; }\n" + "}\n"; + + char const* devNatspec = "{" + "\"methods\":{" + " \"mul\":{ \"details\": \"Multiplies a number by 7\"}" + "}}"; + + char const* userNatspec = "{" + "\"methods\":{" + " \"mul\":{ \"notice\": \"Multiplies `a` by 7\"}" + "}}"; + + checkNatspec(sourceCode, devNatspec, false); + checkNatspec(sourceCode, userNatspec, true); +} + BOOST_AUTO_TEST_CASE(user_multiline_comment) { char const* sourceCode = "contract test {\n" - " /// Multiplies `a` by 7\n" + " /// @notice Multiplies `a` by 7\n" " /// and then adds `b`\n" " function mul_and_add(uint a, uint256 b) returns(uint256 d)\n" " {\n" @@ -102,7 +139,7 @@ BOOST_AUTO_TEST_CASE(user_multiline_comment) char const* natspec = "{" "\"methods\":{" - " \"mul_and_add\":{ \"notice\": \" Multiplies `a` by 7\n and then adds `b`\"}" + " \"mul_and_add\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"}" "}}"; checkNatspec(sourceCode, natspec, true); @@ -111,19 +148,18 @@ BOOST_AUTO_TEST_CASE(user_multiline_comment) BOOST_AUTO_TEST_CASE(user_multiple_functions) { char const* sourceCode = "contract test {\n" - " /// Multiplies `a` by 7\n" - " /// and then adds `b`\n" + " /// @notice Multiplies `a` by 7 and then adds `b`\n" " function mul_and_add(uint a, uint256 b) returns(uint256 d)\n" " {\n" " return (a * 7) + b;\n" " }\n" "\n" - " /// Divides `input` by `div`\n" + " /// @notice Divides `input` by `div`\n" " function divide(uint input, uint div) returns(uint d)\n" " {\n" " return input / div;\n" " }\n" - " /// Subtracts 3 from `input`\n" + " /// @notice Subtracts 3 from `input`\n" " function sub(int input) returns(int d)\n" " {\n" " return input - 3;\n" @@ -132,9 +168,9 @@ BOOST_AUTO_TEST_CASE(user_multiple_functions) char const* natspec = "{" "\"methods\":{" - " \"mul_and_add\":{ \"notice\": \" Multiplies `a` by 7\n and then adds `b`\"}," - " \"divide\":{ \"notice\": \" Divides `input` by `div`\"}," - " \"sub\":{ \"notice\": \" Subtracts 3 from `input`\"}" + " \"mul_and_add\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"}," + " \"divide\":{ \"notice\": \"Divides `input` by `div`\"}," + " \"sub\":{ \"notice\": \"Subtracts 3 from `input`\"}" "}}"; checkNatspec(sourceCode, natspec, true); From 638a784c92659a7e061fc82c27f3a98c97c5d6cf Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Thu, 4 Dec 2014 10:13:31 +0100 Subject: [PATCH 13/26] ide working on macos! --- mix/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix/CMakeLists.txt b/mix/CMakeLists.txt index da214fd29..0252b5226 100644 --- a/mix/CMakeLists.txt +++ b/mix/CMakeLists.txt @@ -61,7 +61,7 @@ target_link_libraries(${EXECUTEABLE} webthree qethereum ethereum evm ethcore dev if (APPLE) # First have qt5 install plugins and frameworks add_custom_command(TARGET ${EXECUTEABLE} POST_BUILD - COMMAND /usr/local/opt/qt5/bin/macdeployqt ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTEABLE}.app + COMMAND /usr/local/opt/qt5/bin/macdeployqt -qmldir=${CMAKE_CURRENT_SOURCE_DIR}/qml ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTEABLE}.app WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) # This tool and next will inspect linked libraries in order to determine which dependencies are required From 5ccf4ca52c9c38e478de8176f18d3b406e58e34b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 4 Dec 2014 14:04:48 +0100 Subject: [PATCH 14/26] Protocol bump. --- libethcore/CommonEth.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index 744e85a27..e68839ed5 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -33,7 +33,7 @@ namespace dev namespace eth { -const unsigned c_protocolVersion = 46; +const unsigned c_protocolVersion = 47; const unsigned c_databaseVersion = 5; static const vector> g_units = From 112c583ddb3b43a7260156ab65694e55aac20130 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 4 Dec 2014 17:19:47 +0100 Subject: [PATCH 15/26] Natspec parsing @param doctags - Plus additional work on generally parsing doctags. One important missing feature is to parse a tag midline - Adding more tests --- libsolidity/InterfaceHandler.cpp | 158 ++++++++++++++++++++++--------- libsolidity/InterfaceHandler.h | 9 ++ test/solidityNatspecJSON.cpp | 117 +++++++++++++++++++---- 3 files changed, 225 insertions(+), 59 deletions(-) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index e7da5e6d5..222711e6a 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -1,3 +1,4 @@ + #include #include #include @@ -72,7 +73,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(std::shared_ auto strPtr = f->getDocumentation(); if (strPtr) { - m_notice.clear(); + resetUser(); parseDocString(*strPtr); user["notice"] = Json::Value(m_notice); methods[f->getName()] = user; @@ -94,10 +95,16 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(std::shared_p auto strPtr = f->getDocumentation(); if (strPtr) { - m_dev.clear(); + resetDev(); parseDocString(*strPtr); method["details"] = Json::Value(m_dev); + Json::Value params(Json::objectValue); + for (auto const& pair: m_params) + { + params[pair.first] = pair.second; + } + method["params"] = params; methods[f->getName()] = method; } } @@ -107,86 +114,151 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(std::shared_p } /* -- private -- */ +void InterfaceHandler::resetUser() +{ + m_notice.clear(); +} + +void InterfaceHandler::resetDev() +{ + m_dev.clear(); + m_params.clear(); +} + +size_t skipLineOrEOS(std::string const& _string, size_t _nlPos) +{ + return (_nlPos == std::string::npos) ? _string.length() : _nlPos + 1; +} + +size_t InterfaceHandler::parseDocTagLine(std::string const& _string, + std::string& _tagString, + size_t _pos, + enum docTagType _tagType) +{ + size_t nlPos = _string.find("\n", _pos); + _tagString += _string.substr(_pos, + nlPos == std::string::npos ? + _string.length() : + nlPos - _pos); + m_lastTag = _tagType; + return skipLineOrEOS(_string, nlPos); +} + +size_t InterfaceHandler::parseDocTagParam(std::string const& _string, size_t _startPos) +{ + // find param name + size_t currPos = _string.find(" ", _startPos); + if (currPos == std::string::npos) + { + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("End of param name not found")); + return currPos; //no end of tag found + } + + auto paramName = _string.substr(_startPos, currPos - _startPos); + + currPos += 1; + size_t nlPos = _string.find("\n", currPos); + auto paramDesc = _string.substr(currPos, + nlPos == std::string::npos ? + _string.length() : + nlPos - currPos); + + m_params.push_back(std::make_pair(paramName, paramDesc)); + + m_lastTag = DOCTAG_PARAM; + return skipLineOrEOS(_string, nlPos); +} + +size_t InterfaceHandler::appendDocTagParam(std::string const& _string, size_t _startPos) +{ + // Should never be called with an empty vector + assert(!m_params.empty()); + + auto pair = m_params.back(); + size_t nlPos = _string.find("\n", _startPos); + pair.second += _string.substr(_startPos, + nlPos == std::string::npos ? + _string.length() : + nlPos - _startPos); + + m_params.at(m_params.size() - 1) = pair; + + return skipLineOrEOS(_string, nlPos); +} + size_t InterfaceHandler::parseDocTag(std::string const& _string, std::string const& _tag, size_t _pos) { - //TODO: This is pretty naive at the moment. e.g. need to check for - // '@' between _pos and \n, remove redundancy e.t.c. + //TODO: need to check for @(start of a tag) between here and the end of line + // for all cases size_t nlPos = _pos; if (m_lastTag == DOCTAG_NONE || _tag != "") { if (_tag == "dev") - { - nlPos = _string.find("\n", _pos); - m_dev += _string.substr(_pos, - nlPos == std::string::npos ? - _string.length() : - nlPos - _pos); - m_lastTag = DOCTAG_DEV; - } + nlPos = parseDocTagLine(_string, m_dev, _pos, DOCTAG_DEV); else if (_tag == "notice") - { - nlPos = _string.find("\n", _pos); - m_notice += _string.substr(_pos, - nlPos == std::string::npos ? - _string.length() : - nlPos - _pos); - m_lastTag = DOCTAG_NOTICE; - } + nlPos = parseDocTagLine(_string, m_notice, _pos, DOCTAG_NOTICE); + else if (_tag == "param") + nlPos = parseDocTagParam(_string, _pos); else { //TODO: Some form of warning } } else + appendDocTag(_string, _pos); + + return nlPos; +} + +size_t InterfaceHandler::appendDocTag(std::string const& _string, size_t _startPos) +{ + size_t newPos = _startPos; + switch(m_lastTag) { - switch(m_lastTag) - { case DOCTAG_DEV: - nlPos = _string.find("\n", _pos); - m_dev += _string.substr(_pos, - nlPos == std::string::npos ? - _string.length() : - nlPos - _pos); + m_dev += " "; + newPos = parseDocTagLine(_string, m_dev, _startPos, DOCTAG_DEV); break; case DOCTAG_NOTICE: - nlPos = _string.find("\n", _pos); - m_notice += _string.substr(_pos, - nlPos == std::string::npos ? - _string.length() : - nlPos - _pos); + m_notice += " "; + newPos = parseDocTagLine(_string, m_notice, _startPos, DOCTAG_NOTICE); + break; + case DOCTAG_PARAM: + newPos = appendDocTagParam(_string, _startPos); break; default: break; } - } - - return nlPos; + return newPos; } void InterfaceHandler::parseDocString(std::string const& _string, size_t _startPos) { size_t pos2; size_t newPos = _startPos; - size_t pos1 = _string.find("@", _startPos); + size_t tagPos = _string.find("@", _startPos); + size_t nlPos = _string.find("\n", _startPos); - if (pos1 != std::string::npos) + if (tagPos != std::string::npos && tagPos < nlPos) { // we found a tag - pos2 = _string.find(" ", pos1); + pos2 = _string.find(" ", tagPos); if (pos2 == std::string::npos) { BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("End of tag not found")); return; //no end of tag found } - newPos = parseDocTag(_string, _string.substr(pos1 + 1, pos2 - pos1 - 1), pos2 + 1); + newPos = parseDocTag(_string, _string.substr(tagPos + 1, pos2 - tagPos - 1), pos2 + 1); } - else + else if (m_lastTag != DOCTAG_NONE) // continuation of the previous tag + newPos = appendDocTag(_string, _startPos + 1); + else // skip the line if a newline was found { - newPos = parseDocTag(_string, "", _startPos + 1); + if (newPos != std::string::npos) + newPos = nlPos + 1; } - - if (newPos == std::string::npos) + if (newPos == std::string::npos || newPos == _string.length()) return; // EOS parseDocString(_string, newPos); } diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index 6f2f2937d..2a70af950 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/InterfaceHandler.h @@ -42,6 +42,7 @@ enum docTagType DOCTAG_NONE = 0, DOCTAG_DEV, DOCTAG_NOTICE, + DOCTAG_PARAM }; class InterfaceHandler @@ -74,7 +75,14 @@ public: std::unique_ptr getDevDocumentation(std::shared_ptr _contractDef); private: + void resetUser(); + void resetDev(); + + size_t parseDocTagLine(std::string const& _string, std::string& _tagString, size_t _pos, enum docTagType _tagType); + size_t parseDocTagParam(std::string const& _string, size_t _startPos); + size_t appendDocTagParam(std::string const& _string, size_t _startPos); void parseDocString(std::string const& _string, size_t _startPos = 0); + size_t appendDocTag(std::string const& _string, size_t _startPos); size_t parseDocTag(std::string const& _string, std::string const& _tag, size_t _pos); Json::StyledWriter m_writer; @@ -83,6 +91,7 @@ private: enum docTagType m_lastTag; std::string m_notice; std::string m_dev; + std::vector> m_params; }; } //solidity NS diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp index d74aa8ab9..5894f3b04 100644 --- a/test/solidityNatspecJSON.cpp +++ b/test/solidityNatspecJSON.cpp @@ -89,21 +89,6 @@ BOOST_AUTO_TEST_CASE(user_basic_test) checkNatspec(sourceCode, natspec, true); } -BOOST_AUTO_TEST_CASE(dev_basic_test) -{ - char const* sourceCode = "contract test {\n" - " /// @dev Multiplies a number by 7\n" - " function mul(uint a) returns(uint d) { return a * 7; }\n" - "}\n"; - - char const* natspec = "{" - "\"methods\":{" - " \"mul\":{ \"details\": \"Multiplies a number by 7\"}" - "}}"; - - checkNatspec(sourceCode, natspec, false); -} - BOOST_AUTO_TEST_CASE(dev_and_user_basic_test) { char const* sourceCode = "contract test {\n" @@ -114,7 +99,11 @@ BOOST_AUTO_TEST_CASE(dev_and_user_basic_test) char const* devNatspec = "{" "\"methods\":{" - " \"mul\":{ \"details\": \"Multiplies a number by 7\"}" + " \"mul\":{ \n" + " \"details\": \"Multiplies a number by 7\",\n" + " \"params\": {}\n" + " }\n" + " }\n" "}}"; char const* userNatspec = "{" @@ -186,6 +175,102 @@ BOOST_AUTO_TEST_CASE(user_empty_contract) checkNatspec(sourceCode, natspec, true); } +BOOST_AUTO_TEST_CASE(dev_multiple_params) +{ + 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 second Documentation for the second parameter\n" + " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul\":{ \n" + " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" + " \"params\": {\n" + " \"a\": \"Documentation for the first parameter\",\n" + " \"second\": \"Documentation for the second parameter\"\n" + " }\n" + " }\n" + "}}"; + + checkNatspec(sourceCode, natspec, false); +} + +BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) +{ + char const* sourceCode = "contract test {\n" + " /// @dev Multiplies a number by 7 and adds second parameter\n" + " /// @param a Documentation for the first parameter starts here.\n" + " /// Since it's a really complicated parameter we need 2 lines\n" + " /// @param second Documentation for the second parameter\n" + " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul\":{ \n" + " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" + " \"params\": {\n" + " \"a\": \"Documentation for the first parameter starts here.Since it's a really complicated parameter we need 2 lines\",\n" + " \"second\": \"Documentation for the second parameter\"\n" + " }\n" + " }\n" + "}}"; + + checkNatspec(sourceCode, natspec, false); +} + +BOOST_AUTO_TEST_CASE(dev_multiple_functions) +{ + 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 second Documentation for the second parameter\n" + " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + " \n" + " /// @dev Divides 2 numbers\n" + " /// @param input Documentation for the input parameter\n" + " /// @param div Documentation for the div parameter\n" + " function divide(uint input, uint div) returns(uint d)\n" + " {\n" + " return input / div;\n" + " }\n" + " /// @dev Subtracts 3 from `input`\n" + " /// @param input Documentation for the input parameter\n" + " function sub(int input) returns(int d)\n" + " {\n" + " return input - 3;\n" + " }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul\":{ \n" + " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" + " \"params\": {\n" + " \"a\": \"Documentation for the first parameter\",\n" + " \"second\": \"Documentation for the second parameter\"\n" + " }\n" + " },\n" + " \"divide\":{ \n" + " \"details\": \"Divides 2 numbers\",\n" + " \"params\": {\n" + " \"input\": \"Documentation for the input parameter\",\n" + " \"div\": \"Documentation for the div parameter\"\n" + " }\n" + " },\n" + " \"sub\":{ \n" + " \"details\": \"Subtracts 3 from `input`\",\n" + " \"params\": {\n" + " \"input\": \"Documentation for the input parameter\"\n" + " }\n" + " }\n" + "}}"; + + checkNatspec(sourceCode, natspec, false); +} BOOST_AUTO_TEST_SUITE_END() From 69bb2a38b97f25b7973e094a89b9f2126aee8023 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 4 Dec 2014 18:12:52 +0100 Subject: [PATCH 16/26] Natspec @return tag parsing - Also omitting tags from the output JSON file if they are missing instead of providing an empty string for their value --- libsolidity/InterfaceHandler.cpp | 32 +++++++++++--- libsolidity/InterfaceHandler.h | 4 +- test/solidityNatspecJSON.cpp | 74 +++++++++++++++++++++++++++++++- 3 files changed, 100 insertions(+), 10 deletions(-) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 222711e6a..2a6de2c35 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -75,8 +75,11 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(std::shared_ { resetUser(); parseDocString(*strPtr); - user["notice"] = Json::Value(m_notice); - methods[f->getName()] = user; + if (!m_notice.empty()) + {// since @notice is the only user tag if missing function should not appear + user["notice"] = Json::Value(m_notice); + methods[f->getName()] = user; + } } } doc["methods"] = methods; @@ -86,6 +89,8 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(std::shared_ std::unique_ptr InterfaceHandler::getDevDocumentation(std::shared_ptr _contractDef) { + // LTODO: Somewhere in this function warnings for mismatch of param names + // should be thrown Json::Value doc; Json::Value methods(Json::objectValue); @@ -98,14 +103,20 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(std::shared_p resetDev(); parseDocString(*strPtr); - method["details"] = Json::Value(m_dev); + if (!m_dev.empty()) + method["details"] = Json::Value(m_dev); Json::Value params(Json::objectValue); for (auto const& pair: m_params) { params[pair.first] = pair.second; } - method["params"] = params; - methods[f->getName()] = method; + if (!m_params.empty()) + method["params"] = params; + if (!m_return.empty()) + method["return"] = m_return; + + if (!method.empty()) // add the function, only if we have any documentation to add + methods[f->getName()] = method; } } doc["methods"] = methods; @@ -122,6 +133,7 @@ void InterfaceHandler::resetUser() void InterfaceHandler::resetDev() { m_dev.clear(); + m_return.clear(); m_params.clear(); } @@ -188,7 +200,7 @@ size_t InterfaceHandler::appendDocTagParam(std::string const& _string, size_t _s size_t InterfaceHandler::parseDocTag(std::string const& _string, std::string const& _tag, size_t _pos) { - //TODO: need to check for @(start of a tag) between here and the end of line + // LTODO: need to check for @(start of a tag) between here and the end of line // for all cases size_t nlPos = _pos; if (m_lastTag == DOCTAG_NONE || _tag != "") @@ -197,11 +209,13 @@ size_t InterfaceHandler::parseDocTag(std::string const& _string, std::string con nlPos = parseDocTagLine(_string, m_dev, _pos, DOCTAG_DEV); else if (_tag == "notice") nlPos = parseDocTagLine(_string, m_notice, _pos, DOCTAG_NOTICE); + else if (_tag == "return") + nlPos = parseDocTagLine(_string, m_return, _pos, DOCTAG_RETURN); else if (_tag == "param") nlPos = parseDocTagParam(_string, _pos); else { - //TODO: Some form of warning + // LTODO: Unknown tas, throw some form of warning } } else @@ -223,6 +237,10 @@ size_t InterfaceHandler::appendDocTag(std::string const& _string, size_t _startP m_notice += " "; newPos = parseDocTagLine(_string, m_notice, _startPos, DOCTAG_NOTICE); break; + case DOCTAG_RETURN: + m_return += " "; + newPos = parseDocTagLine(_string, m_return, _startPos, DOCTAG_RETURN); + break; case DOCTAG_PARAM: newPos = appendDocTagParam(_string, _startPos); break; diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index 2a70af950..eeaed033d 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/InterfaceHandler.h @@ -42,7 +42,8 @@ enum docTagType DOCTAG_NONE = 0, DOCTAG_DEV, DOCTAG_NOTICE, - DOCTAG_PARAM + DOCTAG_PARAM, + DOCTAG_RETURN }; class InterfaceHandler @@ -91,6 +92,7 @@ private: enum docTagType m_lastTag; std::string m_notice; std::string m_dev; + std::string m_return; std::vector> m_params; }; diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp index 5894f3b04..2c4be219b 100644 --- a/test/solidityNatspecJSON.cpp +++ b/test/solidityNatspecJSON.cpp @@ -100,8 +100,7 @@ BOOST_AUTO_TEST_CASE(dev_and_user_basic_test) char const* devNatspec = "{" "\"methods\":{" " \"mul\":{ \n" - " \"details\": \"Multiplies a number by 7\",\n" - " \"params\": {}\n" + " \"details\": \"Multiplies a number by 7\"\n" " }\n" " }\n" "}}"; @@ -175,6 +174,24 @@ BOOST_AUTO_TEST_CASE(user_empty_contract) checkNatspec(sourceCode, natspec, true); } +BOOST_AUTO_TEST_CASE(dev_and_user_no_doc) +{ + char const* sourceCode = "contract test {\n" + " function mul(uint a) returns(uint d) { return a * 7; }\n" + " function sub(int input) returns(int d)\n" + " {\n" + " return input - 3;\n" + " }\n" + "}\n"; + + char const* devNatspec = "{\"methods\":{}}"; + + char const* userNatspec = "{\"methods\":{}}"; + + checkNatspec(sourceCode, devNatspec, false); + checkNatspec(sourceCode, userNatspec, true); +} + BOOST_AUTO_TEST_CASE(dev_multiple_params) { char const* sourceCode = "contract test {\n" @@ -272,6 +289,59 @@ BOOST_AUTO_TEST_CASE(dev_multiple_functions) checkNatspec(sourceCode, natspec, false); } +BOOST_AUTO_TEST_CASE(dev_return) +{ + char const* sourceCode = "contract test {\n" + " /// @dev Multiplies a number by 7 and adds second parameter\n" + " /// @param a Documentation for the first parameter starts here.\n" + " /// Since it's a really complicated parameter we need 2 lines\n" + " /// @param second Documentation for the second parameter\n" + " /// @return The result of the multiplication\n" + " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul\":{ \n" + " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" + " \"params\": {\n" + " \"a\": \"Documentation for the first parameter starts here.Since it's a really complicated parameter we need 2 lines\",\n" + " \"second\": \"Documentation for the second parameter\"\n" + " },\n" + " \"return\": \"The result of the multiplication\"\n" + " }\n" + "}}"; + + checkNatspec(sourceCode, natspec, false); +} + +BOOST_AUTO_TEST_CASE(dev_multiline_return) +{ + char const* sourceCode = "contract test {\n" + " /// @dev Multiplies a number by 7 and adds second parameter\n" + " /// @param a Documentation for the first parameter starts here.\n" + " /// Since it's a really complicated parameter we need 2 lines\n" + " /// @param second Documentation for the second parameter\n" + " /// @return The result of the multiplication\n" + " /// and cookies with nutella\n" + " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul\":{ \n" + " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" + " \"params\": {\n" + " \"a\": \"Documentation for the first parameter starts here.Since it's a really complicated parameter we need 2 lines\",\n" + " \"second\": \"Documentation for the second parameter\"\n" + " },\n" + " \"return\": \"The result of the multiplication and cookies with nutella\"\n" + " }\n" + "}}"; + + checkNatspec(sourceCode, natspec, false); +} + BOOST_AUTO_TEST_SUITE_END() } From 67da8798cf29b428d06905616b8ef42b1b624a45 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 4 Dec 2014 23:55:47 +0100 Subject: [PATCH 17/26] Addressing styling and miscellaneous issue with Natspec --- libsolidity/CompilerStack.cpp | 20 ++++++------- libsolidity/CompilerStack.h | 4 +-- libsolidity/InterfaceHandler.cpp | 49 ++++++++++++++------------------ libsolidity/InterfaceHandler.h | 18 ++++++------ 4 files changed, 43 insertions(+), 48 deletions(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 3e9791bd2..3281442fa 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -36,7 +36,7 @@ namespace dev namespace solidity { -CompilerStack::CompilerStack():m_interfaceHandler(make_shared()){} +CompilerStack::CompilerStack(): m_interfaceHandler(make_shared()) {} void CompilerStack::setSource(string const& _sourceCode) { @@ -84,33 +84,31 @@ void CompilerStack::streamAssembly(ostream& _outStream) m_compiler->streamAssembly(_outStream); } -std::string const* CompilerStack::getJsonDocumentation(enum documentationType _type) +std::string const* CompilerStack::getJsonDocumentation(enum DocumentationType _type) { if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); - auto createOrReturnDoc = [this, _type](std::unique_ptr& _doc) + auto createDocIfNotThere = [this, _type](std::unique_ptr& _doc) { - if(!_doc) - { + if (!_doc) _doc = m_interfaceHandler->getDocumentation(m_contractASTNode, _type); - } }; switch (_type) { case NATSPEC_USER: - createOrReturnDoc(m_userDocumentation); + createDocIfNotThere(m_userDocumentation); return m_userDocumentation.get(); case NATSPEC_DEV: - createOrReturnDoc(m_devDocumentation); + createDocIfNotThere(m_devDocumentation); return m_devDocumentation.get(); case ABI_INTERFACE: - createOrReturnDoc(m_interface); + createDocIfNotThere(m_interface); return m_interface.get(); } - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Internal error")); - return nullptr; + + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); } bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 511951291..de356b1ae 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -37,7 +37,7 @@ class Compiler; class GlobalContext; class InterfaceHandler; -enum documentationType: unsigned short +enum DocumentationType: unsigned short { NATSPEC_USER = 1, NATSPEC_DEV, @@ -74,7 +74,7 @@ public: /// Prerequisite: Successful call to parse or compile. /// @param type The type of the documentation to get. /// Can be one of 3 types defined at @c documentation_type - std::string const* getJsonDocumentation(enum documentationType type); + std::string const* getJsonDocumentation(enum DocumentationType type); /// Returns the previously used scanner, useful for counting lines during error reporting. Scanner const& getScanner() const { return *m_scanner; } diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 2a6de2c35..53632bc6f 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -3,8 +3,10 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ /* -- public -- */ @@ -14,7 +16,7 @@ InterfaceHandler::InterfaceHandler() } std::unique_ptr InterfaceHandler::getDocumentation(std::shared_ptr _contractDef, - enum documentationType _type) + enum DocumentationType _type) { switch(_type) { @@ -34,8 +36,7 @@ std::unique_ptr InterfaceHandler::getABIInterface(std::shared_ptr exportedFunctions = _contractDef->getInterfaceFunctions(); - for (FunctionDefinition const* f: exportedFunctions) + for (FunctionDefinition const* f: _contractDef->getInterfaceFunctions()) { Json::Value method; Json::Value inputs(Json::arrayValue); @@ -107,9 +108,8 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(std::shared_p method["details"] = Json::Value(m_dev); Json::Value params(Json::objectValue); for (auto const& pair: m_params) - { params[pair.first] = pair.second; - } + if (!m_params.empty()) method["params"] = params; if (!m_return.empty()) @@ -145,12 +145,12 @@ size_t skipLineOrEOS(std::string const& _string, size_t _nlPos) size_t InterfaceHandler::parseDocTagLine(std::string const& _string, std::string& _tagString, size_t _pos, - enum docTagType _tagType) + enum DocTagType _tagType) { - size_t nlPos = _string.find("\n", _pos); + size_t nlPos = _string.find('\n', _pos); _tagString += _string.substr(_pos, nlPos == std::string::npos ? - _string.length() : + _string.length() - _pos: nlPos - _pos); m_lastTag = _tagType; return skipLineOrEOS(_string, nlPos); @@ -159,20 +159,18 @@ size_t InterfaceHandler::parseDocTagLine(std::string const& _string, size_t InterfaceHandler::parseDocTagParam(std::string const& _string, size_t _startPos) { // find param name - size_t currPos = _string.find(" ", _startPos); + size_t currPos = _string.find(' ', _startPos); if (currPos == std::string::npos) - { BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("End of param name not found")); - return currPos; //no end of tag found - } + auto paramName = _string.substr(_startPos, currPos - _startPos); currPos += 1; - size_t nlPos = _string.find("\n", currPos); + size_t nlPos = _string.find('\n', currPos); auto paramDesc = _string.substr(currPos, nlPos == std::string::npos ? - _string.length() : + _string.length() - currPos : nlPos - currPos); m_params.push_back(std::make_pair(paramName, paramDesc)); @@ -184,13 +182,13 @@ size_t InterfaceHandler::parseDocTagParam(std::string const& _string, size_t _st size_t InterfaceHandler::appendDocTagParam(std::string const& _string, size_t _startPos) { // Should never be called with an empty vector - assert(!m_params.empty()); - + if (asserts(!m_params.empty())) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Internal: Tried to append to empty parameter")); auto pair = m_params.back(); - size_t nlPos = _string.find("\n", _startPos); + size_t nlPos = _string.find('\n', _startPos); pair.second += _string.substr(_startPos, nlPos == std::string::npos ? - _string.length() : + _string.length() - _startPos : nlPos - _startPos); m_params.at(m_params.size() - 1) = pair; @@ -227,7 +225,7 @@ size_t InterfaceHandler::parseDocTag(std::string const& _string, std::string con size_t InterfaceHandler::appendDocTag(std::string const& _string, size_t _startPos) { size_t newPos = _startPos; - switch(m_lastTag) + switch (m_lastTag) { case DOCTAG_DEV: m_dev += " "; @@ -254,18 +252,15 @@ void InterfaceHandler::parseDocString(std::string const& _string, size_t _startP { size_t pos2; size_t newPos = _startPos; - size_t tagPos = _string.find("@", _startPos); - size_t nlPos = _string.find("\n", _startPos); + size_t tagPos = _string.find('@', _startPos); + size_t nlPos = _string.find('\n', _startPos); if (tagPos != std::string::npos && tagPos < nlPos) { // we found a tag - pos2 = _string.find(" ", tagPos); + pos2 = _string.find(' ', tagPos); if (pos2 == std::string::npos) - { BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("End of tag not found")); - return; //no end of tag found - } newPos = parseDocTag(_string, _string.substr(tagPos + 1, pos2 - tagPos - 1), pos2 + 1); } diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index eeaed033d..e9e3a83c8 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/InterfaceHandler.h @@ -30,14 +30,16 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ // Forward declarations class ContractDefinition; -enum documentationType: unsigned short; +enum DocumentationType: unsigned short; -enum docTagType +enum DocTagType { DOCTAG_NONE = 0, DOCTAG_DEV, @@ -54,11 +56,11 @@ public: /// Get the given type of documentation /// @param _contractDef The contract definition /// @param _type The type of the documentation. Can be one of the - /// types provided by @c documentation_type + /// types provided by @c DocumentationType /// @return A unique pointer contained string with the json /// representation of provided type std::unique_ptr getDocumentation(std::shared_ptr _contractDef, - enum documentationType _type); + enum DocumentationType _type); /// Get the ABI Interface of the contract /// @param _contractDef The contract definition /// @return A unique pointer contained string with the json @@ -79,7 +81,7 @@ private: void resetUser(); void resetDev(); - size_t parseDocTagLine(std::string const& _string, std::string& _tagString, size_t _pos, enum docTagType _tagType); + size_t parseDocTagLine(std::string const& _string, std::string& _tagString, size_t _pos, enum DocTagType _tagType); size_t parseDocTagParam(std::string const& _string, size_t _startPos); size_t appendDocTagParam(std::string const& _string, size_t _startPos); void parseDocString(std::string const& _string, size_t _startPos = 0); @@ -89,7 +91,7 @@ private: Json::StyledWriter m_writer; // internal state - enum docTagType m_lastTag; + enum DocTagType m_lastTag; std::string m_notice; std::string m_dev; std::string m_return; From 4bbb9eb264f3e9cb9a3947c6eed3a60b0645f281 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 5 Dec 2014 02:10:54 +0100 Subject: [PATCH 18/26] Using iterators in Natspec comment parsing - Used iterators in the entirety of the InterfaceHandler natspec comment parsing pipeline - Fixed issue where @param continuing in new line would not get a space --- libsolidity/InterfaceHandler.cpp | 138 +++++++++++++++---------------- libsolidity/InterfaceHandler.h | 20 +++-- test/solidityNatspecJSON.cpp | 6 +- 3 files changed, 82 insertions(+), 82 deletions(-) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 53632bc6f..1450d8fc3 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -137,143 +137,135 @@ void InterfaceHandler::resetDev() m_params.clear(); } -size_t skipLineOrEOS(std::string const& _string, size_t _nlPos) +std::string::const_iterator skipLineOrEOS(std::string::const_iterator _nlPos, + std::string::const_iterator _end) { - return (_nlPos == std::string::npos) ? _string.length() : _nlPos + 1; + return (_nlPos == _end) ? _end : ++_nlPos; } -size_t InterfaceHandler::parseDocTagLine(std::string const& _string, - std::string& _tagString, - size_t _pos, - enum DocTagType _tagType) +std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos, + std::string::const_iterator _end, + std::string& _tagString, + enum DocTagType _tagType) { - size_t nlPos = _string.find('\n', _pos); - _tagString += _string.substr(_pos, - nlPos == std::string::npos ? - _string.length() - _pos: - nlPos - _pos); + auto nlPos = std::find(_pos, _end, '\n'); + std::copy(_pos, nlPos, back_inserter(_tagString)); m_lastTag = _tagType; - return skipLineOrEOS(_string, nlPos); + return skipLineOrEOS(nlPos, _end); } -size_t InterfaceHandler::parseDocTagParam(std::string const& _string, size_t _startPos) +std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::const_iterator _pos, + std::string::const_iterator _end) { // find param name - size_t currPos = _string.find(' ', _startPos); - if (currPos == std::string::npos) + auto currPos = std::find(_pos, _end, ' '); + if (currPos == _end) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("End of param name not found")); - auto paramName = _string.substr(_startPos, currPos - _startPos); + auto paramName = std::string(_pos, currPos); currPos += 1; - size_t nlPos = _string.find('\n', currPos); - auto paramDesc = _string.substr(currPos, - nlPos == std::string::npos ? - _string.length() - currPos : - nlPos - currPos); - + auto nlPos = std::find(currPos, _end, '\n'); + auto paramDesc = std::string(currPos, nlPos); m_params.push_back(std::make_pair(paramName, paramDesc)); m_lastTag = DOCTAG_PARAM; - return skipLineOrEOS(_string, nlPos); + return skipLineOrEOS(nlPos, _end); } -size_t InterfaceHandler::appendDocTagParam(std::string const& _string, size_t _startPos) +std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::const_iterator _pos, + std::string::const_iterator _end) { // Should never be called with an empty vector if (asserts(!m_params.empty())) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Internal: Tried to append to empty parameter")); + auto pair = m_params.back(); - size_t nlPos = _string.find('\n', _startPos); - pair.second += _string.substr(_startPos, - nlPos == std::string::npos ? - _string.length() - _startPos : - nlPos - _startPos); + pair.second += " "; + auto nlPos = std::find(_pos, _end, '\n'); + std::copy(_pos, nlPos, back_inserter(pair.second)); m_params.at(m_params.size() - 1) = pair; - return skipLineOrEOS(_string, nlPos); + return skipLineOrEOS(nlPos, _end); } -size_t InterfaceHandler::parseDocTag(std::string const& _string, std::string const& _tag, size_t _pos) +std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_iterator _pos, + std::string::const_iterator _end, + std::string const& _tag) { // LTODO: need to check for @(start of a tag) between here and the end of line // for all cases - size_t nlPos = _pos; if (m_lastTag == DOCTAG_NONE || _tag != "") { if (_tag == "dev") - nlPos = parseDocTagLine(_string, m_dev, _pos, DOCTAG_DEV); + return parseDocTagLine(_pos, _end, m_dev, DOCTAG_DEV); else if (_tag == "notice") - nlPos = parseDocTagLine(_string, m_notice, _pos, DOCTAG_NOTICE); + return parseDocTagLine(_pos, _end, m_notice, DOCTAG_NOTICE); else if (_tag == "return") - nlPos = parseDocTagLine(_string, m_return, _pos, DOCTAG_RETURN); + return parseDocTagLine(_pos, _end, m_return, DOCTAG_RETURN); else if (_tag == "param") - nlPos = parseDocTagParam(_string, _pos); + return parseDocTagParam(_pos, _end); else { - // LTODO: Unknown tas, throw some form of warning + // LTODO: Unknown tag, throw some form of warning and not just an exception + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Unknown tag encountered")); } } else - appendDocTag(_string, _pos); - - return nlPos; + return appendDocTag(_pos, _end); } -size_t InterfaceHandler::appendDocTag(std::string const& _string, size_t _startPos) +std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_iterator _pos, + std::string::const_iterator _end) { - size_t newPos = _startPos; switch (m_lastTag) { case DOCTAG_DEV: m_dev += " "; - newPos = parseDocTagLine(_string, m_dev, _startPos, DOCTAG_DEV); - break; + return parseDocTagLine(_pos, _end, m_dev, DOCTAG_DEV); case DOCTAG_NOTICE: m_notice += " "; - newPos = parseDocTagLine(_string, m_notice, _startPos, DOCTAG_NOTICE); - break; + return parseDocTagLine(_pos, _end, m_notice, DOCTAG_NOTICE); case DOCTAG_RETURN: m_return += " "; - newPos = parseDocTagLine(_string, m_return, _startPos, DOCTAG_RETURN); - break; + return parseDocTagLine(_pos, _end, m_return, DOCTAG_RETURN); case DOCTAG_PARAM: - newPos = appendDocTagParam(_string, _startPos); - break; + return appendDocTagParam(_pos, _end); default: + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Internal: Illegal documentation tag")); break; - } - return newPos; + } } -void InterfaceHandler::parseDocString(std::string const& _string, size_t _startPos) +void InterfaceHandler::parseDocString(std::string const& _string) { - size_t pos2; - size_t newPos = _startPos; - size_t tagPos = _string.find('@', _startPos); - size_t nlPos = _string.find('\n', _startPos); + auto currPos = _string.begin(); + auto end = _string.end(); - if (tagPos != std::string::npos && tagPos < nlPos) + while (currPos != end) { - // we found a tag - pos2 = _string.find(' ', tagPos); - if (pos2 == std::string::npos) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("End of tag not found")); + auto tagPos = std::find(currPos, end, '@'); + auto nlPos = std::find(currPos, end, '\n'); - newPos = parseDocTag(_string, _string.substr(tagPos + 1, pos2 - tagPos - 1), pos2 + 1); - } - else if (m_lastTag != DOCTAG_NONE) // continuation of the previous tag - newPos = appendDocTag(_string, _startPos + 1); - else // skip the line if a newline was found - { - if (newPos != std::string::npos) - newPos = nlPos + 1; + if (tagPos != end && tagPos < nlPos) + { + // we found a tag + auto tagNameEndPos = std::find(tagPos, end, ' '); + if (tagNameEndPos == end) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("End of tag not found")); + + currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos +1, tagNameEndPos)); + } + else if (m_lastTag != DOCTAG_NONE) // continuation of the previous tag + currPos = appendDocTag(currPos + 1, end); + else // skip the line if a newline was found + { + if (currPos != end) + currPos = nlPos + 1; + } } - if (newPos == std::string::npos || newPos == _string.length()) - return; // EOS - parseDocString(_string, newPos); } } //solidity NS diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index e9e3a83c8..7a5ee66db 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/InterfaceHandler.h @@ -81,12 +81,20 @@ private: void resetUser(); void resetDev(); - size_t parseDocTagLine(std::string const& _string, std::string& _tagString, size_t _pos, enum DocTagType _tagType); - size_t parseDocTagParam(std::string const& _string, size_t _startPos); - size_t appendDocTagParam(std::string const& _string, size_t _startPos); - void parseDocString(std::string const& _string, size_t _startPos = 0); - size_t appendDocTag(std::string const& _string, size_t _startPos); - size_t parseDocTag(std::string const& _string, std::string const& _tag, size_t _pos); + std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos, + std::string::const_iterator _end, + std::string& _tagString, + enum DocTagType _tagType); + std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos, + std::string::const_iterator _end); + std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos, + std::string::const_iterator _end); + void parseDocString(std::string const& _string); + std::string::const_iterator appendDocTag(std::string::const_iterator _pos, + std::string::const_iterator _end); + std::string::const_iterator parseDocTag(std::string::const_iterator _pos, + std::string::const_iterator _end, + std::string const& _tag); Json::StyledWriter m_writer; diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp index 2c4be219b..f2ae15c96 100644 --- a/test/solidityNatspecJSON.cpp +++ b/test/solidityNatspecJSON.cpp @@ -230,7 +230,7 @@ BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) " \"mul\":{ \n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"params\": {\n" - " \"a\": \"Documentation for the first parameter starts here.Since it's a really complicated parameter we need 2 lines\",\n" + " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" " \"second\": \"Documentation for the second parameter\"\n" " }\n" " }\n" @@ -305,7 +305,7 @@ BOOST_AUTO_TEST_CASE(dev_return) " \"mul\":{ \n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"params\": {\n" - " \"a\": \"Documentation for the first parameter starts here.Since it's a really complicated parameter we need 2 lines\",\n" + " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" " \"second\": \"Documentation for the second parameter\"\n" " },\n" " \"return\": \"The result of the multiplication\"\n" @@ -332,7 +332,7 @@ BOOST_AUTO_TEST_CASE(dev_multiline_return) " \"mul\":{ \n" " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" " \"params\": {\n" - " \"a\": \"Documentation for the first parameter starts here.Since it's a really complicated parameter we need 2 lines\",\n" + " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" " \"second\": \"Documentation for the second parameter\"\n" " },\n" " \"return\": \"The result of the multiplication and cookies with nutella\"\n" From c96e1d0c36c7b1a627a3b99591fc0e40ddc39bf5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 5 Dec 2014 09:37:10 +0100 Subject: [PATCH 19/26] Logs from subcalls are folded in. --- libevm/ExtVMFace.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 9e6601d0a..7f7ae7a50 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -88,6 +88,7 @@ struct SubState { suicides += _s.suicides; refunds += _s.refunds; + logs += _s.logs; return *this; } }; From 4cf9f82bc7344207555f4e9f52a4352ba5b6424d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 5 Dec 2014 09:43:29 +0100 Subject: [PATCH 20/26] Windows fix & used code removed --- libevm/ExtVMFace.h | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 7f7ae7a50..4a175ff4e 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -36,20 +36,12 @@ namespace dev namespace eth { -template inline std::set toSet(std::vector const& _ts) -{ - std::set ret; - for (auto const& t: _ts) - ret.insert(t); - return ret; -} - using LogBloom = h512; struct LogEntry { LogEntry() {} - LogEntry(RLP const& _r) { address = (Address)_r[0]; topics = (h256s)_r[1]; data = _r[2].toBytes(); } + LogEntry(RLP const& _r) { address = (Address)_r[0]; topics = _r[1].toVector(); data = _r[2].toBytes(); } LogEntry(Address const& _address, h256s const& _ts, bytes&& _d): address(_address), topics(_ts), data(std::move(_d)) {} void streamRLP(RLPStream& _s) const { _s.appendList(3) << address << topics << data; } From 45499094ef6fb2822da6494cc898c49f90eacbd0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 5 Dec 2014 09:52:34 +0100 Subject: [PATCH 21/26] PV48. --- libethcore/CommonEth.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethcore/CommonEth.cpp b/libethcore/CommonEth.cpp index e68839ed5..5f9332ad8 100644 --- a/libethcore/CommonEth.cpp +++ b/libethcore/CommonEth.cpp @@ -33,7 +33,7 @@ namespace dev namespace eth { -const unsigned c_protocolVersion = 47; +const unsigned c_protocolVersion = 48; const unsigned c_databaseVersion = 5; static const vector> g_units = From 7de687fece7631adca5a78f27d0222c5f911caed Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 5 Dec 2014 10:47:05 +0100 Subject: [PATCH 22/26] Block-level receipts dump. --- alethzero/MainWin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 4a32f66b2..2a66a6d69 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1285,6 +1285,7 @@ void Main::on_blocks_currentItemChanged() s << "
" << sha3(i.data()).abridged();// << ": " << i[1].toHash() << " [" << i[2].toInt() << " used]"; s << "
Post: " << info.stateRoot << ""; s << "
Dump: " << toHex(block[0].data()) << ""; + s << "
Receipts-Hex: " << toHex(ethereum()->blockChain().receipts(h).rlp()) << "
"; } else { From cfb8e74a75acac98042a0aef2d26ccfaf6749647 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 5 Dec 2014 12:08:26 +0100 Subject: [PATCH 23/26] Introducing Docstring parsing error exception and style fixes --- libsolidity/Exceptions.h | 1 + libsolidity/InterfaceHandler.cpp | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/libsolidity/Exceptions.h b/libsolidity/Exceptions.h index 1903c1dc2..8298c9810 100644 --- a/libsolidity/Exceptions.h +++ b/libsolidity/Exceptions.h @@ -36,6 +36,7 @@ struct TypeError: virtual Exception {}; struct DeclarationError: virtual Exception {}; struct CompilerError: virtual Exception {}; struct InternalCompilerError: virtual Exception {}; +struct DocstringParsingError: virtual Exception {}; typedef boost::error_info errinfo_sourcePosition; typedef boost::error_info errinfo_sourceLocation; diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 1450d8fc3..ca02cc37d 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -28,7 +28,7 @@ std::unique_ptr InterfaceHandler::getDocumentation(std::shared_ptr< return getABIInterface(_contractDef); } - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Internal error")); + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type")); return nullptr; } @@ -160,7 +160,7 @@ std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::cons // find param name auto currPos = std::find(_pos, _end, ' '); if (currPos == _end) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("End of param name not found")); + BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("End of param name not found" + std::string(_pos, _end))); auto paramName = std::string(_pos, currPos); @@ -179,7 +179,7 @@ std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::con { // Should never be called with an empty vector if (asserts(!m_params.empty())) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Internal: Tried to append to empty parameter")); + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Tried to append to empty parameter")); auto pair = m_params.back(); pair.second += " "; @@ -210,7 +210,7 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite else { // LTODO: Unknown tag, throw some form of warning and not just an exception - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Unknown tag encountered")); + BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("Unknown tag " + _tag + " encountered")); } } else @@ -234,7 +234,7 @@ std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_it case DOCTAG_PARAM: return appendDocTagParam(_pos, _end); default: - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Internal: Illegal documentation tag")); + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type")); break; } } @@ -254,17 +254,15 @@ void InterfaceHandler::parseDocString(std::string const& _string) // we found a tag auto tagNameEndPos = std::find(tagPos, end, ' '); if (tagNameEndPos == end) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("End of tag not found")); + BOOST_THROW_EXCEPTION(DocstringParsingError() << + errinfo_comment("End of tag " + std::string(tagPos, tagNameEndPos) + "not found")); - currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos +1, tagNameEndPos)); + currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos)); } else if (m_lastTag != DOCTAG_NONE) // continuation of the previous tag currPos = appendDocTag(currPos + 1, end); - else // skip the line if a newline was found - { - if (currPos != end) - currPos = nlPos + 1; - } + else if (currPos != end) // skip the line if a newline was found + currPos = nlPos + 1; } } From 9977229d759448bf0a6c89a4451bae8852ed02c1 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 5 Dec 2014 12:27:18 +0100 Subject: [PATCH 24/26] Newline right after doctag is now a valid natspec entry - Plus tests for that --- libsolidity/InterfaceHandler.cpp | 14 +++++++-- test/solidityNatspecJSON.cpp | 51 ++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index ca02cc37d..0115c7f59 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -137,8 +137,8 @@ void InterfaceHandler::resetDev() m_params.clear(); } -std::string::const_iterator skipLineOrEOS(std::string::const_iterator _nlPos, - std::string::const_iterator _end) +static inline std::string::const_iterator skipLineOrEOS(std::string::const_iterator _nlPos, + std::string::const_iterator _end) { return (_nlPos == _end) ? _end : ++_nlPos; } @@ -239,6 +239,14 @@ std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_it } } +static inline std::string::const_iterator getFirstSpaceOrNl(std::string::const_iterator _pos, + std::string::const_iterator _end) +{ + auto spacePos = std::find(_pos, _end, ' '); + auto nlPos = std::find(_pos, _end, '\n'); + return (spacePos < nlPos) ? spacePos : nlPos; +} + void InterfaceHandler::parseDocString(std::string const& _string) { auto currPos = _string.begin(); @@ -252,7 +260,7 @@ void InterfaceHandler::parseDocString(std::string const& _string) if (tagPos != end && tagPos < nlPos) { // we found a tag - auto tagNameEndPos = std::find(tagPos, end, ' '); + auto tagNameEndPos = getFirstSpaceOrNl(tagPos, end); if (tagNameEndPos == end) BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("End of tag " + std::string(tagPos, tagNameEndPos) + "not found")); diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp index f2ae15c96..9596f2b80 100644 --- a/test/solidityNatspecJSON.cpp +++ b/test/solidityNatspecJSON.cpp @@ -192,6 +192,30 @@ BOOST_AUTO_TEST_CASE(dev_and_user_no_doc) checkNatspec(sourceCode, userNatspec, true); } +BOOST_AUTO_TEST_CASE(dev_desc_after_nl) +{ + char const* sourceCode = "contract test {\n" + " /// @dev\n" + " /// Multiplies a number by 7 and adds second parameter\n" + " /// @param a Documentation for the first parameter\n" + " /// @param second Documentation for the second parameter\n" + " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul\":{ \n" + " \"details\": \" Multiplies a number by 7 and adds second parameter\",\n" + " \"params\": {\n" + " \"a\": \"Documentation for the first parameter\",\n" + " \"second\": \"Documentation for the second parameter\"\n" + " }\n" + " }\n" + "}}"; + + checkNatspec(sourceCode, natspec, false); +} + BOOST_AUTO_TEST_CASE(dev_multiple_params) { char const* sourceCode = "contract test {\n" @@ -314,6 +338,33 @@ BOOST_AUTO_TEST_CASE(dev_return) checkNatspec(sourceCode, natspec, false); } +BOOST_AUTO_TEST_CASE(dev_return_desc_after_nl) +{ + char const* sourceCode = "contract test {\n" + " /// @dev Multiplies a number by 7 and adds second parameter\n" + " /// @param a Documentation for the first parameter starts here.\n" + " /// Since it's a really complicated parameter we need 2 lines\n" + " /// @param second Documentation for the second parameter\n" + " /// @return\n" + " /// The result of the multiplication\n" + " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + "}\n"; + + char const* natspec = "{" + "\"methods\":{" + " \"mul\":{ \n" + " \"details\": \"Multiplies a number by 7 and adds second parameter\",\n" + " \"params\": {\n" + " \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n" + " \"second\": \"Documentation for the second parameter\"\n" + " },\n" + " \"return\": \" The result of the multiplication\"\n" + " }\n" + "}}"; + + checkNatspec(sourceCode, natspec, false); +} + BOOST_AUTO_TEST_CASE(dev_multiline_return) { From 78938ac468c4eddedd0c8c66a12094668e224668 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 5 Dec 2014 12:41:32 +0100 Subject: [PATCH 25/26] Stack compiler now correctly returns a string and not a pointer --- libsolidity/CompilerStack.cpp | 8 ++++---- libsolidity/CompilerStack.h | 2 +- test/solidityJSONInterfaceTest.cpp | 2 +- test/solidityNatspecJSON.cpp | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 3281442fa..c83257a1d 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -84,7 +84,7 @@ void CompilerStack::streamAssembly(ostream& _outStream) m_compiler->streamAssembly(_outStream); } -std::string const* CompilerStack::getJsonDocumentation(enum DocumentationType _type) +std::string const& CompilerStack::getJsonDocumentation(enum DocumentationType _type) { if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); @@ -99,13 +99,13 @@ std::string const* CompilerStack::getJsonDocumentation(enum DocumentationType _t { case NATSPEC_USER: createDocIfNotThere(m_userDocumentation); - return m_userDocumentation.get(); + return *m_userDocumentation; case NATSPEC_DEV: createDocIfNotThere(m_devDocumentation); - return m_devDocumentation.get(); + return *m_devDocumentation; case ABI_INTERFACE: createDocIfNotThere(m_interface); - return m_interface.get(); + return *m_interface; } BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index de356b1ae..8dc546fbe 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -74,7 +74,7 @@ public: /// Prerequisite: Successful call to parse or compile. /// @param type The type of the documentation to get. /// Can be one of 3 types defined at @c documentation_type - std::string const* getJsonDocumentation(enum DocumentationType type); + std::string const& getJsonDocumentation(enum DocumentationType type); /// Returns the previously used scanner, useful for counting lines during error reporting. Scanner const& getScanner() const { return *m_scanner; } diff --git a/test/solidityJSONInterfaceTest.cpp b/test/solidityJSONInterfaceTest.cpp index 8fe0ea653..407f05d03 100644 --- a/test/solidityJSONInterfaceTest.cpp +++ b/test/solidityJSONInterfaceTest.cpp @@ -50,7 +50,7 @@ public: msg += *extra; BOOST_FAIL(msg); } - std::string generatedInterfaceString = *m_compilerStack.getJsonDocumentation(ABI_INTERFACE); + std::string generatedInterfaceString = m_compilerStack.getJsonDocumentation(ABI_INTERFACE); Json::Value generatedInterface; m_reader.parse(generatedInterfaceString, generatedInterface); Json::Value expectedInterface; diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp index 9596f2b80..2ccedf7a2 100644 --- a/test/solidityNatspecJSON.cpp +++ b/test/solidityNatspecJSON.cpp @@ -55,9 +55,9 @@ public: } if (_userDocumentation) - generatedDocumentationString = *m_compilerStack.getJsonDocumentation(NATSPEC_USER); + generatedDocumentationString = m_compilerStack.getJsonDocumentation(NATSPEC_USER); else - generatedDocumentationString = *m_compilerStack.getJsonDocumentation(NATSPEC_DEV); + generatedDocumentationString = m_compilerStack.getJsonDocumentation(NATSPEC_DEV); Json::Value generatedDocumentation; m_reader.parse(generatedDocumentationString, generatedDocumentation); Json::Value expectedDocumentation; From 8857a7a75e8095ef7940deda220a256208e1ba90 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 5 Dec 2014 15:50:39 +0100 Subject: [PATCH 26/26] Replacing old cstyle enums with c++11 enums in natspec --- libsolidity/CompilerStack.cpp | 8 +++---- libsolidity/CompilerStack.h | 4 ++-- libsolidity/InterfaceHandler.cpp | 38 +++++++++++++++--------------- libsolidity/InterfaceHandler.h | 20 ++++++++-------- solc/main.cpp | 6 ++--- test/solidityJSONInterfaceTest.cpp | 2 +- test/solidityNatspecJSON.cpp | 4 ++-- 7 files changed, 41 insertions(+), 41 deletions(-) diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index c83257a1d..01bf99d94 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -84,7 +84,7 @@ void CompilerStack::streamAssembly(ostream& _outStream) m_compiler->streamAssembly(_outStream); } -std::string const& CompilerStack::getJsonDocumentation(enum DocumentationType _type) +std::string const& CompilerStack::getJsonDocumentation(DocumentationType _type) { if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); @@ -97,13 +97,13 @@ std::string const& CompilerStack::getJsonDocumentation(enum DocumentationType _t switch (_type) { - case NATSPEC_USER: + case DocumentationType::NATSPEC_USER: createDocIfNotThere(m_userDocumentation); return *m_userDocumentation; - case NATSPEC_DEV: + case DocumentationType::NATSPEC_DEV: createDocIfNotThere(m_devDocumentation); return *m_devDocumentation; - case ABI_INTERFACE: + case DocumentationType::ABI_INTERFACE: createDocIfNotThere(m_interface); return *m_interface; } diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 8dc546fbe..928815cc5 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -37,7 +37,7 @@ class Compiler; class GlobalContext; class InterfaceHandler; -enum DocumentationType: unsigned short +enum class DocumentationType: uint8_t { NATSPEC_USER = 1, NATSPEC_DEV, @@ -74,7 +74,7 @@ public: /// Prerequisite: Successful call to parse or compile. /// @param type The type of the documentation to get. /// Can be one of 3 types defined at @c documentation_type - std::string const& getJsonDocumentation(enum DocumentationType type); + std::string const& getJsonDocumentation(DocumentationType type); /// Returns the previously used scanner, useful for counting lines during error reporting. Scanner const& getScanner() const { return *m_scanner; } diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index 0115c7f59..18c053cb1 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -12,19 +12,19 @@ namespace solidity InterfaceHandler::InterfaceHandler() { - m_lastTag = DOCTAG_NONE; + m_lastTag = DocTagType::NONE; } std::unique_ptr InterfaceHandler::getDocumentation(std::shared_ptr _contractDef, - enum DocumentationType _type) + DocumentationType _type) { switch(_type) { - case NATSPEC_USER: + case DocumentationType::NATSPEC_USER: return getUserDocumentation(_contractDef); - case NATSPEC_DEV: + case DocumentationType::NATSPEC_DEV: return getDevDocumentation(_contractDef); - case ABI_INTERFACE: + case DocumentationType::ABI_INTERFACE: return getABIInterface(_contractDef); } @@ -146,7 +146,7 @@ static inline std::string::const_iterator skipLineOrEOS(std::string::const_itera std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos, std::string::const_iterator _end, std::string& _tagString, - enum DocTagType _tagType) + DocTagType _tagType) { auto nlPos = std::find(_pos, _end, '\n'); std::copy(_pos, nlPos, back_inserter(_tagString)); @@ -170,7 +170,7 @@ std::string::const_iterator InterfaceHandler::parseDocTagParam(std::string::cons auto paramDesc = std::string(currPos, nlPos); m_params.push_back(std::make_pair(paramName, paramDesc)); - m_lastTag = DOCTAG_PARAM; + m_lastTag = DocTagType::PARAM; return skipLineOrEOS(nlPos, _end); } @@ -197,14 +197,14 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite { // LTODO: need to check for @(start of a tag) between here and the end of line // for all cases - if (m_lastTag == DOCTAG_NONE || _tag != "") + if (m_lastTag == DocTagType::NONE || _tag != "") { if (_tag == "dev") - return parseDocTagLine(_pos, _end, m_dev, DOCTAG_DEV); + return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV); else if (_tag == "notice") - return parseDocTagLine(_pos, _end, m_notice, DOCTAG_NOTICE); + return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); else if (_tag == "return") - return parseDocTagLine(_pos, _end, m_return, DOCTAG_RETURN); + return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); else if (_tag == "param") return parseDocTagParam(_pos, _end); else @@ -222,16 +222,16 @@ std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_it { switch (m_lastTag) { - case DOCTAG_DEV: + case DocTagType::DEV: m_dev += " "; - return parseDocTagLine(_pos, _end, m_dev, DOCTAG_DEV); - case DOCTAG_NOTICE: + return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV); + case DocTagType::NOTICE: m_notice += " "; - return parseDocTagLine(_pos, _end, m_notice, DOCTAG_NOTICE); - case DOCTAG_RETURN: + return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); + case DocTagType::RETURN: m_return += " "; - return parseDocTagLine(_pos, _end, m_return, DOCTAG_RETURN); - case DOCTAG_PARAM: + return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); + case DocTagType::PARAM: return appendDocTagParam(_pos, _end); default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type")); @@ -267,7 +267,7 @@ void InterfaceHandler::parseDocString(std::string const& _string) currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos)); } - else if (m_lastTag != DOCTAG_NONE) // continuation of the previous tag + else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag currPos = appendDocTag(currPos + 1, end); else if (currPos != end) // skip the line if a newline was found currPos = nlPos + 1; diff --git a/libsolidity/InterfaceHandler.h b/libsolidity/InterfaceHandler.h index 7a5ee66db..524e2903c 100644 --- a/libsolidity/InterfaceHandler.h +++ b/libsolidity/InterfaceHandler.h @@ -37,15 +37,15 @@ namespace solidity // Forward declarations class ContractDefinition; -enum DocumentationType: unsigned short; +enum class DocumentationType: uint8_t; -enum DocTagType +enum class DocTagType: uint8_t { - DOCTAG_NONE = 0, - DOCTAG_DEV, - DOCTAG_NOTICE, - DOCTAG_PARAM, - DOCTAG_RETURN + NONE = 0, + DEV, + NOTICE, + PARAM, + RETURN }; class InterfaceHandler @@ -60,7 +60,7 @@ public: /// @return A unique pointer contained string with the json /// representation of provided type std::unique_ptr getDocumentation(std::shared_ptr _contractDef, - enum DocumentationType _type); + DocumentationType _type); /// Get the ABI Interface of the contract /// @param _contractDef The contract definition /// @return A unique pointer contained string with the json @@ -84,7 +84,7 @@ private: std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos, std::string::const_iterator _end, std::string& _tagString, - enum DocTagType _tagType); + DocTagType _tagType); std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos, std::string::const_iterator _end); std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos, @@ -99,7 +99,7 @@ private: Json::StyledWriter m_writer; // internal state - enum DocTagType m_lastTag; + DocTagType m_lastTag; std::string m_notice; std::string m_dev; std::string m_return; diff --git a/solc/main.cpp b/solc/main.cpp index daeb2707c..21ffc98cc 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -135,9 +135,9 @@ int main(int argc, char** argv) cout << "Opcodes:" << endl; cout << eth::disassemble(compiler.getBytecode()) << endl; cout << "Binary: " << toHex(compiler.getBytecode()) << endl; - cout << "Interface specification: " << compiler.getJsonDocumentation(ABI_INTERFACE) << endl; - cout << "Natspec user documentation: " << compiler.getJsonDocumentation(NATSPEC_USER) << endl; - cout << "Natspec developer documentation: " << compiler.getJsonDocumentation(NATSPEC_DEV) << endl; + cout << "Interface specification: " << compiler.getJsonDocumentation(DocumentationType::ABI_INTERFACE) << endl; + cout << "Natspec user documentation: " << compiler.getJsonDocumentation(DocumentationType::NATSPEC_USER) << endl; + cout << "Natspec developer documentation: " << compiler.getJsonDocumentation(DocumentationType::NATSPEC_DEV) << endl; return 0; } diff --git a/test/solidityJSONInterfaceTest.cpp b/test/solidityJSONInterfaceTest.cpp index 407f05d03..17e97dc69 100644 --- a/test/solidityJSONInterfaceTest.cpp +++ b/test/solidityJSONInterfaceTest.cpp @@ -50,7 +50,7 @@ public: msg += *extra; BOOST_FAIL(msg); } - std::string generatedInterfaceString = m_compilerStack.getJsonDocumentation(ABI_INTERFACE); + std::string generatedInterfaceString = m_compilerStack.getJsonDocumentation(DocumentationType::ABI_INTERFACE); Json::Value generatedInterface; m_reader.parse(generatedInterfaceString, generatedInterface); Json::Value expectedInterface; diff --git a/test/solidityNatspecJSON.cpp b/test/solidityNatspecJSON.cpp index 2ccedf7a2..5e4c1fca7 100644 --- a/test/solidityNatspecJSON.cpp +++ b/test/solidityNatspecJSON.cpp @@ -55,9 +55,9 @@ public: } if (_userDocumentation) - generatedDocumentationString = m_compilerStack.getJsonDocumentation(NATSPEC_USER); + generatedDocumentationString = m_compilerStack.getJsonDocumentation(DocumentationType::NATSPEC_USER); else - generatedDocumentationString = m_compilerStack.getJsonDocumentation(NATSPEC_DEV); + generatedDocumentationString = m_compilerStack.getJsonDocumentation(DocumentationType::NATSPEC_DEV); Json::Value generatedDocumentation; m_reader.parse(generatedDocumentationString, generatedDocumentation); Json::Value expectedDocumentation;