solidity/libsolidity/parsing/DocStringParser.cpp

185 lines
4.9 KiB
C++
Raw Normal View History

2018-12-17 18:28:10 +00:00
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
2015-10-26 14:13:36 +00:00
#include <libsolidity/parsing/DocStringParser.h>
2018-12-17 18:28:10 +00:00
#include <libsolidity/ast/AST.h>
#include <liblangutil/Common.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Exceptions.h>
2015-10-26 14:13:36 +00:00
#include <range/v3/algorithm/find_first_of.hpp>
#include <range/v3/algorithm/find_if_not.hpp>
#include <range/v3/view/subrange.hpp>
2017-01-27 10:19:48 +00:00
2015-10-26 14:13:36 +00:00
using namespace std;
2019-12-11 16:31:36 +00:00
using namespace solidity;
using namespace solidity::langutil;
using namespace solidity::frontend;
2015-10-26 14:13:36 +00:00
2017-01-27 10:19:48 +00:00
namespace
{
string::const_iterator skipLineOrEOS(
2015-10-26 14:13:36 +00:00
string::const_iterator _nlPos,
string::const_iterator _end
)
{
return (_nlPos == _end) ? _end : ++_nlPos;
}
string::const_iterator firstNonIdentifier(
2015-10-26 14:13:36 +00:00
string::const_iterator _pos,
string::const_iterator _end
)
{
auto currPos = _pos;
if (currPos == _pos && isIdentifierStart(*currPos))
{
currPos++;
currPos = ranges::find_if_not(ranges::make_subrange(currPos, _end), isIdentifierPart);
}
return currPos;
2015-10-26 14:13:36 +00:00
}
2017-01-27 10:19:48 +00:00
string::const_iterator firstWhitespaceOrNewline(
string::const_iterator _pos,
string::const_iterator _end
)
{
return ranges::find_first_of(ranges::make_subrange(_pos, _end), " \t\n");
}
2017-01-27 10:19:48 +00:00
string::const_iterator skipWhitespace(
string::const_iterator _pos,
string::const_iterator _end
)
{
auto isWhitespace = [](char const& c) { return (c == ' ' || c == '\t'); };
return ranges::find_if_not(ranges::make_subrange(_pos, _end), isWhitespace);
}
2017-01-27 10:19:48 +00:00
}
multimap<string, DocTag> DocStringParser::parse()
2015-10-26 14:13:36 +00:00
{
m_lastTag = nullptr;
m_docTags = {};
2015-10-26 14:13:36 +00:00
solAssert(m_node.text(), "");
iter currPos = m_node.text()->begin();
iter end = m_node.text()->end();
2015-10-26 14:13:36 +00:00
while (currPos != end)
{
iter tagPos = find(currPos, end, '@');
iter nlPos = find(currPos, end, '\n');
2015-10-26 14:13:36 +00:00
if (tagPos != end && tagPos < nlPos)
{
// we found a tag
iter tagNameEndPos = firstWhitespaceOrNewline(tagPos, end);
string tagName{tagPos + 1, tagNameEndPos};
iter tagDataPos = (tagNameEndPos != end) ? tagNameEndPos + 1 : tagNameEndPos;
currPos = parseDocTag(tagDataPos, end, tagName);
2015-10-26 14:13:36 +00:00
}
else if (!!m_lastTag) // continuation of the previous tag
currPos = parseDocTagLine(currPos, end, true);
2015-10-26 14:13:36 +00:00
else if (currPos != end)
{
// if it begins without a tag then consider it as @notice
if (currPos == m_node.text()->begin())
2015-10-26 14:13:36 +00:00
{
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;
}
}
2022-08-23 17:28:45 +00:00
return std::move(m_docTags);
2015-10-26 14:13:36 +00:00
}
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')
2015-10-26 14:13:36 +00:00
m_lastTag->content += " ";
2016-12-01 09:14:19 +00:00
else if (!_appending)
_pos = skipWhitespace(_pos, _end);
2015-10-26 14:13:36 +00:00
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, m_node.location(), "No param name given");
return _end;
}
auto nameEndPos = firstNonIdentifier(nameStartPos, _end);
auto paramName = string(nameStartPos, nameEndPos);
2015-10-26 14:13:36 +00:00
auto descStartPos = skipWhitespace(nameEndPos, _end);
auto nlPos = find(descStartPos, _end, '\n');
if (descStartPos == nlPos)
{
m_errorReporter.docstringParsingError(9942_error, m_node.location(), "No description given for param " + paramName);
return _end;
}
2015-10-26 14:13:36 +00:00
auto paramDesc = string(descStartPos, nlPos);
2015-10-26 14:13:36 +00:00
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)
{
2020-05-01 22:37:25 +00:00
// TODO: need to check for @(start of a tag) between here and the end of line
2015-10-26 14:13:36 +00:00
// 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);
2015-10-26 14:13:36 +00:00
}
void DocStringParser::newTag(string const& _tagName)
{
m_lastTag = &m_docTags.insert(make_pair(_tagName, DocTag()))->second;
}