/* This file is part of solidity. solidity 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. solidity 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 solidity. If not, see . */ // SPDX-License-Identifier: GPL-3.0 #include #include #include #include #include #include using namespace std; using namespace solidity; using namespace solidity::langutil; using namespace solidity::frontend; namespace { string::const_iterator skipLineOrEOS( string::const_iterator _nlPos, string::const_iterator _end ) { return (_nlPos == _end) ? _end : ++_nlPos; } string::const_iterator firstNonIdentifier( string::const_iterator _pos, string::const_iterator _end ) { auto currPos = _pos; if (currPos == _pos && isIdentifierStart(*currPos)) { currPos++; while (currPos != _end && isIdentifierPart(*currPos)) currPos++; } return currPos; } string::const_iterator firstWhitespaceOrNewline( string::const_iterator _pos, string::const_iterator _end ) { return boost::range::find_first_of(make_pair(_pos, _end), " \t\n"); } string::const_iterator skipWhitespace( string::const_iterator _pos, string::const_iterator _end ) { auto currPos = _pos; while (currPos != _end && (*currPos == ' ' || *currPos == '\t')) currPos += 1; return currPos; } } void DocStringParser::parse(string const& _docString, ErrorReporter& _errorReporter) { m_errorReporter = &_errorReporter; m_lastTag = nullptr; auto currPos = _docString.begin(); auto end = _docString.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 = firstWhitespaceOrNewline(tagPos, end); auto tagName = string(tagPos + 1, tagNameEndPos); auto tagDataPos = (tagNameEndPos != end) ? tagNameEndPos + 1 : tagNameEndPos; currPos = parseDocTag(tagDataPos, end, tagName); } else if (!!m_lastTag) // continuation of the previous tag currPos = parseDocTagLine(currPos, end, true); else if (currPos != end) { // if it begins without a tag then consider it as @notice if (currPos == _docString.begin()) { currPos = parseDocTag(currPos, end, "notice"); continue; } else if (nlPos == end) //end of text break; // else skip the line if a newline was found and we get here currPos = nlPos + 1; } } } DocStringParser::iter DocStringParser::parseDocTagLine(iter _pos, iter _end, bool _appending) { solAssert(!!m_lastTag, ""); auto nlPos = find(_pos, _end, '\n'); if (_appending && _pos != _end && *_pos != ' ' && *_pos != '\t') m_lastTag->content += " "; else if (!_appending) _pos = skipWhitespace(_pos, _end); copy(_pos, nlPos, back_inserter(m_lastTag->content)); return skipLineOrEOS(nlPos, _end); } DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end) { // find param name start auto nameStartPos = skipWhitespace(_pos, _end); if (nameStartPos == _end) { m_errorReporter->docstringParsingError(3335_error, "No param name given"); return _end; } auto nameEndPos = firstNonIdentifier(nameStartPos, _end); auto paramName = string(nameStartPos, nameEndPos); auto descStartPos = skipWhitespace(nameEndPos, _end); auto nlPos = find(descStartPos, _end, '\n'); if (descStartPos == nlPos) { m_errorReporter->docstringParsingError(9942_error, "No description given for param " + paramName); return _end; } auto paramDesc = string(descStartPos, nlPos); newTag("param"); m_lastTag->paramName = paramName; m_lastTag->content = paramDesc; return skipLineOrEOS(nlPos, _end); } DocStringParser::iter DocStringParser::parseDocTag(iter _pos, iter _end, string const& _tag) { // LTODO: need to check for @(start of a tag) between here and the end of line // for all cases. if (!m_lastTag || _tag != "") { if (_tag == "param") return parseDocTagParam(_pos, _end); else { newTag(_tag); return parseDocTagLine(_pos, _end, false); } } else return parseDocTagLine(_pos, _end, true); } void DocStringParser::newTag(string const& _tagName) { m_lastTag = &m_docTags.insert(make_pair(_tagName, DocTag()))->second; }