diff options
author | chriseth <c@ethdev.com> | 2015-10-26 22:13:36 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2015-10-26 22:24:36 +0800 |
commit | b4f561680a2a5169d1245271245e2b71822cb73a (patch) | |
tree | 6e3acecc9bbe825400e4297a32ff641df27d1943 /libsolidity/interface | |
parent | d6e77ce0e1da577e5f2c000f89b4fba3505d84a0 (diff) | |
download | dexon-solidity-b4f561680a2a5169d1245271245e2b71822cb73a.tar.gz dexon-solidity-b4f561680a2a5169d1245271245e2b71822cb73a.tar.zst dexon-solidity-b4f561680a2a5169d1245271245e2b71822cb73a.zip |
Store docstrings in AST annotations.
Diffstat (limited to 'libsolidity/interface')
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 22 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.h | 3 | ||||
-rw-r--r-- | libsolidity/interface/InterfaceHandler.cpp | 318 | ||||
-rw-r--r-- | libsolidity/interface/InterfaceHandler.h | 55 |
4 files changed, 64 insertions, 334 deletions
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 775c7eb6..6b55b408 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -28,6 +28,7 @@ #include <libsolidity/analysis/GlobalContext.h> #include <libsolidity/analysis/NameAndTypeResolver.h> #include <libsolidity/analysis/TypeChecker.h> +#include <libsolidity/analysis/DocStringAnalyser.h> #include <libsolidity/codegen/Compiler.h> #include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/InterfaceHandler.h> @@ -114,6 +115,12 @@ bool CompilerStack::parse() resolveImports(); + bool noErrors = true; + DocStringAnalyser docStringAnalyser(m_errors); + for (Source const* source: m_sourceOrder) + if (!docStringAnalyser.analyseDocStrings(*source->ast)) + noErrors = false; + m_globalContext = make_shared<GlobalContext>(); NameAndTypeResolver resolver(m_globalContext->declarations(), m_errors); for (Source const* source: m_sourceOrder) @@ -131,8 +138,6 @@ bool CompilerStack::parse() m_contracts[contract->name()].contract = contract; } - InterfaceHandler interfaceHandler; - bool typesFine = true; for (Source const* source: m_sourceOrder) for (ASTPointer<ASTNode> const& node: source->ast->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) @@ -142,15 +147,15 @@ bool CompilerStack::parse() TypeChecker typeChecker(m_errors); if (typeChecker.checkTypeRequirements(*contract)) { - contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract)); - contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract)); + contract->setDevDocumentation(InterfaceHandler::devDocumentation(*contract)); + contract->setUserDocumentation(InterfaceHandler::userDocumentation(*contract)); } else - typesFine = false; + noErrors = false; m_contracts[contract->name()].contract = contract; } - m_parseSuccessful = typesFine; + m_parseSuccessful = noErrors; return m_parseSuccessful; } @@ -287,7 +292,7 @@ string const& CompilerStack::metadata(string const& _contractName, Documentation // caches the result if (!*doc) - doc->reset(new string(currentContract.interfaceHandler->documentation(*currentContract.contract, _type))); + doc->reset(new string(InterfaceHandler::documentation(*currentContract.contract, _type))); return *(*doc); } @@ -428,8 +433,5 @@ CompilerStack::Source const& CompilerStack::source(string const& _sourceName) co return it->second; } -CompilerStack::Contract::Contract(): interfaceHandler(make_shared<InterfaceHandler>()) {} - - } } diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 1f1b74f5..ac71da2e 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -188,13 +188,10 @@ private: eth::LinkerObject object; eth::LinkerObject runtimeObject; eth::LinkerObject cloneObject; - std::shared_ptr<InterfaceHandler> interfaceHandler; mutable std::unique_ptr<std::string const> interface; mutable std::unique_ptr<std::string const> solidityInterface; mutable std::unique_ptr<std::string const> userDocumentation; mutable std::unique_ptr<std::string const> devDocumentation; - - Contract(); }; void resolveImports(); diff --git a/libsolidity/interface/InterfaceHandler.cpp b/libsolidity/interface/InterfaceHandler.cpp index 136136e6..30cd9724 100644 --- a/libsolidity/interface/InterfaceHandler.cpp +++ b/libsolidity/interface/InterfaceHandler.cpp @@ -3,19 +3,10 @@ #include <boost/range/irange.hpp> #include <libsolidity/ast/AST.h> #include <libsolidity/interface/CompilerStack.h> -using namespace std; - -namespace dev -{ -namespace solidity -{ - -/* -- public -- */ -InterfaceHandler::InterfaceHandler() -{ - m_lastTag = DocTagType::None; -} +using namespace std; +using namespace dev; +using namespace dev::solidity; string InterfaceHandler::documentation( ContractDefinition const& _contractDef, @@ -181,20 +172,18 @@ string InterfaceHandler::userDocumentation(ContractDefinition const& _contractDe Json::Value methods(Json::objectValue); for (auto const& it: _contractDef.interfaceFunctions()) - { - Json::Value user; - auto strPtr = it.second->documentation(); - if (strPtr) - { - resetUser(); - 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); - methods[it.second->externalSignature()] = user; + if (it.second->hasDeclaration()) + if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration())) + { + string value = extractDoc(f->annotation().docTags, "notice"); + if (!value.empty()) + { + Json::Value user; + // since @notice is the only user tag if missing function should not appear + user["notice"] = Json::Value(value); + methods[it.second->externalSignature()] = user; + } } - } - } doc["methods"] = methods; return Json::StyledWriter().write(doc); @@ -202,60 +191,45 @@ string InterfaceHandler::userDocumentation(ContractDefinition const& _contractDe string InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef) { - // LTODO: Somewhere in this function warnings for mismatch of param names - // should be thrown Json::Value doc; Json::Value methods(Json::objectValue); - auto contractDoc = _contractDef.documentation(); - 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; - } + auto author = extractDoc(_contractDef.annotation().docTags, "author"); + if (!author.empty()) + doc["author"] = author; + auto title = extractDoc(_contractDef.annotation().docTags, "title"); + if (!title.empty()) + doc["title"] = title; for (auto const& it: _contractDef.interfaceFunctions()) { + if (!it.second->hasDeclaration()) + continue; Json::Value method; - auto strPtr = it.second->documentation(); - if (strPtr) + if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration())) { - resetDev(); - parseDocString(*strPtr, CommentOwner::Function); + auto dev = extractDoc(fun->annotation().docTags, "dev"); + if (!dev.empty()) + method["details"] = Json::Value(dev); - if (!m_dev.empty()) - method["details"] = Json::Value(m_dev); + auto author = extractDoc(fun->annotation().docTags, "author"); + if (!author.empty()) + method["author"] = author; - if (!m_author.empty()) - method["author"] = m_author; + auto ret = extractDoc(fun->annotation().docTags, "return"); + if (!ret.empty()) + method["return"] = ret; Json::Value params(Json::objectValue); - vector<string> paramNames = it.second->parameterNames(); - for (auto const& pair: m_params) - { - if (find(paramNames.begin(), paramNames.end(), pair.first) == paramNames.end()) - // LTODO: mismatching parameter name, throw some form of warning and not just an exception - BOOST_THROW_EXCEPTION( - Error(Error::Type::DocstringParsingError) << - errinfo_comment("documented parameter \"" + pair.first + "\" not found in the parameter list of the function.") - ); - params[pair.first] = pair.second; - } + auto paramRange = fun->annotation().docTags.equal_range("param"); + for (auto i = paramRange.first; i != paramRange.second; ++i) + params[i->second.paramName] = Json::Value(i->second.content); - if (!m_params.empty()) + if (!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 + if (!method.empty()) + // add the function, only if we have any documentation to add methods[it.second->externalSignature()] = method; } } @@ -264,215 +238,11 @@ string InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef return Json::StyledWriter().write(doc); } -/* -- private -- */ -void InterfaceHandler::resetUser() +string InterfaceHandler::extractDoc(multimap<string, DocTag> const& _tags, string const& _name) { - m_notice.clear(); + string value; + auto range = _tags.equal_range(_name); + for (auto i = range.first; i != range.second; i++) + value += i->second.content; + return value; } - -void InterfaceHandler::resetDev() -{ - m_dev.clear(); - m_author.clear(); - m_return.clear(); - m_params.clear(); -} - -static inline string::const_iterator skipLineOrEOS( - string::const_iterator _nlPos, - string::const_iterator _end -) -{ - return (_nlPos == _end) ? _end : ++_nlPos; -} - -string::const_iterator InterfaceHandler::parseDocTagLine( - string::const_iterator _pos, - string::const_iterator _end, - string& _tagString, - DocTagType _tagType, - bool _appending -) -{ - auto nlPos = find(_pos, _end, '\n'); - if (_appending && _pos < _end && *_pos != ' ') - _tagString += " "; - copy(_pos, nlPos, back_inserter(_tagString)); - m_lastTag = _tagType; - return skipLineOrEOS(nlPos, _end); -} - -string::const_iterator InterfaceHandler::parseDocTagParam( - string::const_iterator _pos, - string::const_iterator _end -) -{ - // find param name - auto currPos = find(_pos, _end, ' '); - if (currPos == _end) - BOOST_THROW_EXCEPTION(Error(Error::Type::DocstringParsingError) << errinfo_comment("End of param name not found" + string(_pos, _end))); - - - auto paramName = string(_pos, currPos); - - currPos += 1; - auto nlPos = find(currPos, _end, '\n'); - auto paramDesc = string(currPos, nlPos); - m_params.push_back(make_pair(paramName, paramDesc)); - - m_lastTag = DocTagType::Param; - return skipLineOrEOS(nlPos, _end); -} - -string::const_iterator InterfaceHandler::appendDocTagParam( - string::const_iterator _pos, - string::const_iterator _end -) -{ - // Should never be called with an empty vector - solAssert(!m_params.empty(), "Internal: Tried to append to empty parameter"); - - auto pair = m_params.back(); - if (_pos < _end && *_pos != ' ') - pair.second += " "; - auto nlPos = find(_pos, _end, '\n'); - copy(_pos, nlPos, back_inserter(pair.second)); - - m_params.at(m_params.size() - 1) = pair; - - return skipLineOrEOS(nlPos, _end); -} - -string::const_iterator InterfaceHandler::parseDocTag( - string::const_iterator _pos, - string::const_iterator _end, - string const& _tag, - CommentOwner _owner -) -{ - // LTODO: need to check for @(start of a tag) between here and the end of line - // 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") - return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, false); - else if (_tag == "notice") - return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, false); - else if (_tag == "return") - return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, false); - else if (_tag == "author") - { - if (_owner == CommentOwner::Contract) - return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, false); - else if (_owner == CommentOwner::Function) - return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, false); - else - // LTODO: for now this else makes no sense but later comments will go to more language constructs - BOOST_THROW_EXCEPTION(Error(Error::Type::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, false); - else - // LTODO: Unknown tag, throw some form of warning and not just an exception - BOOST_THROW_EXCEPTION(Error(Error::Type::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(Error(Error::Type::DocstringParsingError) << errinfo_comment("Unknown tag " + _tag + " encountered")); - } - else - return appendDocTag(_pos, _end, _owner); -} - -string::const_iterator InterfaceHandler::appendDocTag( - string::const_iterator _pos, - string::const_iterator _end, - CommentOwner _owner -) -{ - switch (m_lastTag) - { - case DocTagType::Dev: - return parseDocTagLine(_pos, _end, m_dev, DocTagType::Dev, true); - case DocTagType::Notice: - return parseDocTagLine(_pos, _end, m_notice, DocTagType::Notice, true); - case DocTagType::Return: - return parseDocTagLine(_pos, _end, m_return, DocTagType::Return, true); - case DocTagType::Author: - if (_owner == CommentOwner::Contract) - return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::Author, true); - else if (_owner == CommentOwner::Function) - return parseDocTagLine(_pos, _end, m_author, DocTagType::Author, true); - else - // LTODO: Unknown tag, throw some form of warning and not just an exception - BOOST_THROW_EXCEPTION(Error(Error::Type::DocstringParsingError) << errinfo_comment("@author tag in illegal comment")); - case DocTagType::Title: - if (_owner == CommentOwner::Contract) - return parseDocTagLine(_pos, _end, m_title, DocTagType::Title, true); - else - // LTODO: Unknown tag, throw some form of warning and not just an exception - BOOST_THROW_EXCEPTION(Error(Error::Type::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; - } -} - -static inline string::const_iterator firstSpaceOrNl( - string::const_iterator _pos, - string::const_iterator _end -) -{ - auto spacePos = find(_pos, _end, ' '); - auto nlPos = find(_pos, _end, '\n'); - return (spacePos < nlPos) ? spacePos : nlPos; -} - -void InterfaceHandler::parseDocString(string const& _string, CommentOwner _owner) -{ - auto currPos = _string.begin(); - auto end = _string.end(); - - while (currPos != end) - { - auto tagPos = find(currPos, end, '@'); - auto nlPos = find(currPos, end, '\n'); - - if (tagPos != end && tagPos < nlPos) - { - // we found a tag - auto tagNameEndPos = firstSpaceOrNl(tagPos, end); - if (tagNameEndPos == end) - BOOST_THROW_EXCEPTION( - Error(Error::Type::DocstringParsingError) << - errinfo_comment("End of tag " + string(tagPos, tagNameEndPos) + "not found")); - - currPos = parseDocTag(tagNameEndPos + 1, end, string(tagPos + 1, tagNameEndPos), _owner); - } - else if (m_lastTag != DocTagType::None) // continuation of the previous tag - currPos = appendDocTag(currPos, end, _owner); - else if (currPos != end) - { - // if it begins without a tag then consider it as @notice - if (currPos == _string.begin()) - { - currPos = parseDocTag(currPos, end, "notice", CommentOwner::Function); - continue; - } - else if (nlPos == end) //end of text - return; - // else skip the line if a newline was found and we get here - currPos = nlPos + 1; - } - } -} - -} //solidity NS -} // dev NS diff --git a/libsolidity/interface/InterfaceHandler.h b/libsolidity/interface/InterfaceHandler.h index 62164517..30b8f520 100644 --- a/libsolidity/interface/InterfaceHandler.h +++ b/libsolidity/interface/InterfaceHandler.h @@ -37,6 +37,7 @@ namespace solidity // Forward declarations class ContractDefinition; +struct DocTag; enum class DocumentationType: uint8_t; enum class DocTagType: uint8_t @@ -59,73 +60,33 @@ enum class CommentOwner 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 DocumentationType /// @return A string with the json representation of provided type - std::string documentation( + static std::string documentation( ContractDefinition const& _contractDef, DocumentationType _type ); /// Get the ABI Interface of the contract /// @param _contractDef The contract definition /// @return A string with the json representation of the contract's ABI Interface - std::string abiInterface(ContractDefinition const& _contractDef); - std::string ABISolidityInterface(ContractDefinition const& _contractDef); + static std::string abiInterface(ContractDefinition const& _contractDef); + static std::string ABISolidityInterface(ContractDefinition const& _contractDef); /// Get the User documentation of the contract /// @param _contractDef The contract definition /// @return A string with the json representation of the contract's user documentation - std::string userDocumentation(ContractDefinition const& _contractDef); + static std::string userDocumentation(ContractDefinition const& _contractDef); /// Genereates the Developer's documentation of the contract /// @param _contractDef The contract definition /// @return A string with the json representation /// of the contract's developer documentation - std::string devDocumentation(ContractDefinition const& _contractDef); + static std::string devDocumentation(ContractDefinition const& _contractDef); private: - void resetUser(); - void resetDev(); - - std::string::const_iterator parseDocTagLine( - std::string::const_iterator _pos, - std::string::const_iterator _end, - std::string& _tagString, - DocTagType _tagType, - bool _appending - ); - 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, CommentOwner _owner); - std::string::const_iterator appendDocTag( - std::string::const_iterator _pos, - 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, - CommentOwner _owner - ); - - // internal state - DocTagType m_lastTag; - 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; + /// Returns concatenation of all content under the given tag name. + static std::string extractDoc(std::multimap<std::string, DocTag> const& _tags, std::string const& _name); }; } //solidity NS |