diff options
author | chriseth <c@ethdev.com> | 2014-12-11 02:23:57 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2014-12-11 02:23:57 +0800 |
commit | c7c189cac0313320a2c8e69350dde5c31a1786c0 (patch) | |
tree | 2f84ae2b009acd352424ccd3253a92e8f4539308 | |
parent | 3c377f77bbe75e65159e79a61696ba906ac22bda (diff) | |
parent | aebd1490bded59e063bb60237ff5a3285b1bcea1 (diff) | |
download | dexon-solidity-c7c189cac0313320a2c8e69350dde5c31a1786c0.tar dexon-solidity-c7c189cac0313320a2c8e69350dde5c31a1786c0.tar.gz dexon-solidity-c7c189cac0313320a2c8e69350dde5c31a1786c0.tar.bz2 dexon-solidity-c7c189cac0313320a2c8e69350dde5c31a1786c0.tar.lz dexon-solidity-c7c189cac0313320a2c8e69350dde5c31a1786c0.tar.xz dexon-solidity-c7c189cac0313320a2c8e69350dde5c31a1786c0.tar.zst dexon-solidity-c7c189cac0313320a2c8e69350dde5c31a1786c0.zip |
Merge pull request #586 from LefterisJP/natspec_contract_tags
Natspec title and author tag.
-rw-r--r-- | AST.h | 9 | ||||
-rw-r--r-- | InterfaceHandler.cpp | 112 | ||||
-rw-r--r-- | InterfaceHandler.h | 21 | ||||
-rw-r--r-- | Parser.cpp | 5 |
4 files changed, 116 insertions, 31 deletions
@@ -156,13 +156,15 @@ class ContractDefinition: public Declaration public: ContractDefinition(Location const& _location, ASTPointer<ASTString> const& _name, + ASTPointer<ASTString> const& _documentation, std::vector<ASTPointer<StructDefinition>> const& _definedStructs, std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions): Declaration(_location, _name), m_definedStructs(_definedStructs), m_stateVariables(_stateVariables), - m_definedFunctions(_definedFunctions) + m_definedFunctions(_definedFunctions), + m_documentation(_documentation) {} virtual void accept(ASTVisitor& _visitor) override; @@ -172,6 +174,10 @@ public: std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; } std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; } + /// @return A shared pointer of an ASTString. + /// Can contain a nullptr in which case indicates absence of documentation + ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; } + /// Returns the functions that make up the calling interface in the intended order. std::vector<FunctionDefinition const*> getInterfaceFunctions() const; @@ -179,6 +185,7 @@ private: std::vector<ASTPointer<StructDefinition>> m_definedStructs; std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; + ASTPointer<ASTString> m_documentation; }; class StructDefinition: public Declaration diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index c3e62cad..f26088af 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -75,7 +75,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi if (strPtr) { resetUser(); - parseDocString(*strPtr); + parseDocString(*strPtr, CommentOwner::FUNCTION); if (!m_notice.empty()) {// since @notice is the only user tag if missing function should not appear user["notice"] = Json::Value(m_notice); @@ -95,6 +95,20 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin Json::Value doc; Json::Value methods(Json::objectValue); + auto contractDoc = _contractDef.getDocumentation(); + if (contractDoc) + { + m_contractAuthor.clear(); + m_title.clear(); + parseDocString(*contractDoc, CommentOwner::CONTRACT); + + if (!m_contractAuthor.empty()) + doc["author"] = m_contractAuthor; + + if (!m_title.empty()) + doc["title"] = m_title; + } + for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) { Json::Value method; @@ -102,16 +116,21 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin if (strPtr) { resetDev(); - parseDocString(*strPtr); + parseDocString(*strPtr, CommentOwner::FUNCTION); if (!m_dev.empty()) method["details"] = Json::Value(m_dev); + + if (!m_author.empty()) + method["author"] = m_author; + 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()) method["return"] = m_return; @@ -133,6 +152,7 @@ void InterfaceHandler::resetUser() void InterfaceHandler::resetDev() { m_dev.clear(); + m_author.clear(); m_return.clear(); m_params.clear(); } @@ -193,10 +213,12 @@ std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::con std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_iterator _pos, std::string::const_iterator _end, - std::string const& _tag) + std::string const& _tag, + CommentOwner _owner) { // LTODO: need to check for @(start of a tag) between here and the end of line - // for all cases + // for all cases. Also somehow automate list of acceptable tags for each + // language construct since current way does not scale well. if (m_lastTag == DocTagType::NONE || _tag != "") { if (_tag == "dev") @@ -205,37 +227,77 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); else if (_tag == "return") return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); + else if (_tag == "author") + { + if (_owner == CommentOwner::CONTRACT) + return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR); + else if (_owner == CommentOwner::FUNCTION) + return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR); + else + // LTODO: for now this else makes no sense but later comments will go to more language constructs + BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts")); + } + else if (_tag == "title") + { + if (_owner == CommentOwner::CONTRACT) + return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE); + else + // LTODO: Unknown tag, throw some form of warning and not just an exception + BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts")); + } else if (_tag == "param") return parseDocTagParam(_pos, _end); else - { // LTODO: Unknown tag, throw some form of warning and not just an exception BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("Unknown tag " + _tag + " encountered")); - } } else - return appendDocTag(_pos, _end); + return appendDocTag(_pos, _end, _owner); } std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_iterator _pos, - std::string::const_iterator _end) + std::string::const_iterator _end, + CommentOwner _owner) { switch (m_lastTag) { - case DocTagType::DEV: - m_dev += " "; - return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV); - case DocTagType::NOTICE: - m_notice += " "; - return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); - case DocTagType::RETURN: - m_return += " "; - 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")); - break; + case DocTagType::DEV: + m_dev += " "; + return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV); + case DocTagType::NOTICE: + m_notice += " "; + return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); + case DocTagType::RETURN: + m_return += " "; + return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); + case DocTagType::AUTHOR: + if (_owner == CommentOwner::CONTRACT) + { + m_contractAuthor += " "; + return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR); + } + else if (_owner == CommentOwner::FUNCTION) + { + m_author += " "; + return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR); + } + else + // LTODO: Unknown tag, throw some form of warning and not just an exception + BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment")); + case DocTagType::TITLE: + if (_owner == CommentOwner::CONTRACT) + { + m_title += " "; + return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE); + } + else + // LTODO: Unknown tag, throw some form of warning and not just an exception + BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment")); + case DocTagType::PARAM: + return appendDocTagParam(_pos, _end); + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type")); + break; } } @@ -247,7 +309,7 @@ static inline std::string::const_iterator getFirstSpaceOrNl(std::string::const_i return (spacePos < nlPos) ? spacePos : nlPos; } -void InterfaceHandler::parseDocString(std::string const& _string) +void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _owner) { auto currPos = _string.begin(); auto end = _string.end(); @@ -265,10 +327,10 @@ void InterfaceHandler::parseDocString(std::string const& _string) 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), _owner); } else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag - currPos = appendDocTag(currPos + 1, end); + currPos = appendDocTag(currPos + 1, end, _owner); else if (currPos != end) // skip the line if a newline was found currPos = nlPos + 1; } diff --git a/InterfaceHandler.h b/InterfaceHandler.h index e6be9e6a..d71345b9 100644 --- a/InterfaceHandler.h +++ b/InterfaceHandler.h @@ -45,7 +45,15 @@ enum class DocTagType: uint8_t DEV, NOTICE, PARAM, - RETURN + RETURN, + AUTHOR, + TITLE +}; + +enum class CommentOwner +{ + CONTRACT, + FUNCTION }; class InterfaceHandler @@ -89,12 +97,14 @@ private: 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); + void parseDocString(std::string const& _string, CommentOwner _owner); std::string::const_iterator appendDocTag(std::string::const_iterator _pos, - std::string::const_iterator _end); + std::string::const_iterator _end, + CommentOwner _owner); std::string::const_iterator parseDocTag(std::string::const_iterator _pos, std::string::const_iterator _end, - std::string const& _tag); + std::string const& _tag, + CommentOwner _owner); Json::StyledWriter m_writer; @@ -103,6 +113,9 @@ private: std::string m_notice; std::string m_dev; std::string m_return; + std::string m_contractAuthor; + std::string m_author; + std::string m_title; std::vector<std::pair<std::string, std::string>> m_params; }; @@ -112,6 +112,9 @@ ASTPointer<ImportDirective> Parser::parseImportDirective() ASTPointer<ContractDefinition> Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); + ASTPointer<ASTString> docstring; + if (m_scanner->getCurrentCommentLiteral() != "") + docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral()); expectToken(Token::CONTRACT); ASTPointer<ASTString> name = expectIdentifierToken(); expectToken(Token::LBRACE); @@ -146,7 +149,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition() } nodeFactory.markEndPosition(); expectToken(Token::RBRACE); - return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions); + return nodeFactory.createNode<ContractDefinition>(name, docstring, structs, stateVariables, functions); } ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic) |