diff options
author | Lefteris Karapetsas <lefteris@refu.co> | 2014-12-10 20:13:12 +0800 |
---|---|---|
committer | Lefteris Karapetsas <lefteris@refu.co> | 2014-12-10 20:24:48 +0800 |
commit | fbc35003cee590b24bbbf8b68dad1cf4a81073c5 (patch) | |
tree | 7e83f579f063575b58ca294a02e2dc21cb817717 | |
parent | 57e6827cb57708ed8f687bf9b0a304b1481cf742 (diff) | |
download | dexon-solidity-fbc35003cee590b24bbbf8b68dad1cf4a81073c5.tar dexon-solidity-fbc35003cee590b24bbbf8b68dad1cf4a81073c5.tar.gz dexon-solidity-fbc35003cee590b24bbbf8b68dad1cf4a81073c5.tar.bz2 dexon-solidity-fbc35003cee590b24bbbf8b68dad1cf4a81073c5.tar.lz dexon-solidity-fbc35003cee590b24bbbf8b68dad1cf4a81073c5.tar.xz dexon-solidity-fbc35003cee590b24bbbf8b68dad1cf4a81073c5.tar.zst dexon-solidity-fbc35003cee590b24bbbf8b68dad1cf4a81073c5.zip |
Natspec title and author tag.
- Adding the title and author natspec documentation tags for contracts
- Also using the author tag for functions now
- Tests
-rw-r--r-- | AST.h | 9 | ||||
-rw-r--r-- | InterfaceHandler.cpp | 109 | ||||
-rw-r--r-- | InterfaceHandler.h | 21 | ||||
-rw-r--r-- | Parser.cpp | 5 |
4 files changed, 114 insertions, 30 deletions
@@ -146,13 +146,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; @@ -161,6 +163,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; @@ -168,6 +174,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..ac1fb29a 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,7 +213,8 @@ 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 @@ -205,37 +226,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 +308,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 +326,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) |