Merge pull request #8221 from ethereum/structured-docs-error-locations

[parser] Source locations for structured documentation errors
This commit is contained in:
chriseth 2020-02-18 14:57:28 +01:00 committed by GitHub
commit c2e22d4cab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 74 additions and 23 deletions

View File

@ -3,6 +3,8 @@
Language Features: Language Features:
* Allow contract types and enums as keys for mappings. * Allow contract types and enums as keys for mappings.
* Allow function selectors to be used as compile-time constants. * Allow function selectors to be used as compile-time constants.
* Report source locations for structured documentation errors.
Compiler Features: Compiler Features:

View File

@ -244,3 +244,12 @@ void ErrorReporter::docstringParsingError(string const& _description)
_description _description
); );
} }
void ErrorReporter::docstringParsingError(SourceLocation const& _location, string const& _description)
{
error(
Error::Type::DocstringParsingError,
_location,
_description
);
}

View File

@ -107,6 +107,7 @@ public:
void fatalTypeError(SourceLocation const& _location, SecondarySourceLocation const& _secondLocation, std::string const& _description); void fatalTypeError(SourceLocation const& _location, SecondarySourceLocation const& _secondLocation, std::string const& _description);
void docstringParsingError(std::string const& _description); void docstringParsingError(std::string const& _description);
void docstringParsingError(SourceLocation const& _location, std::string const& _description);
ErrorList const& errors() const; ErrorList const& errors() const;

View File

@ -306,19 +306,24 @@ bool Scanner::tryScanEndOfLine()
return false; return false;
} }
Token Scanner::scanSingleLineDocComment() int Scanner::scanSingleLineDocComment()
{ {
LiteralScope literal(this, LITERAL_TYPE_COMMENT); LiteralScope literal(this, LITERAL_TYPE_COMMENT);
int endPosition = m_source->position();
advance(); //consume the last '/' at /// advance(); //consume the last '/' at ///
skipWhitespaceExceptUnicodeLinebreak(); skipWhitespaceExceptUnicodeLinebreak();
while (!isSourcePastEndOfInput()) while (!isSourcePastEndOfInput())
{ {
endPosition = m_source->position();
if (tryScanEndOfLine()) if (tryScanEndOfLine())
{ {
// check if next line is also a documentation comment // Check if next line is also a single-line comment.
skipWhitespace(); // If any whitespaces were skipped, use source position before.
if (!skipWhitespace())
endPosition = m_source->position();
if (!m_source->isPastEndOfInput(3) && if (!m_source->isPastEndOfInput(3) &&
m_source->get(0) == '/' && m_source->get(0) == '/' &&
m_source->get(1) == '/' && m_source->get(1) == '/' &&
@ -338,7 +343,7 @@ Token Scanner::scanSingleLineDocComment()
advance(); advance();
} }
literal.complete(); literal.complete();
return Token::CommentLiteral; return endPosition;
} }
Token Scanner::skipMultiLineComment() Token Scanner::skipMultiLineComment()
@ -426,12 +431,10 @@ Token Scanner::scanSlash()
else if (m_char == '/') else if (m_char == '/')
{ {
// doxygen style /// comment // doxygen style /// comment
Token comment;
m_skippedComments[NextNext].location.start = firstSlashPosition; m_skippedComments[NextNext].location.start = firstSlashPosition;
m_skippedComments[NextNext].location.source = m_source; m_skippedComments[NextNext].location.source = m_source;
comment = scanSingleLineDocComment(); m_skippedComments[NextNext].token = Token::CommentLiteral;
m_skippedComments[NextNext].location.end = sourcePos(); m_skippedComments[NextNext].location.end = scanSingleLineDocComment();
m_skippedComments[NextNext].token = comment;
return Token::Whitespace; return Token::Whitespace;
} }
else else

View File

@ -229,7 +229,8 @@ private:
Token scanString(); Token scanString();
Token scanHexString(); Token scanHexString();
Token scanSingleLineDocComment(); /// Scans a single line comment and returns its corrected end position.
int scanSingleLineDocComment();
Token scanMultiLineDocComment(); Token scanMultiLineDocComment();
/// Scans a slash '/' and depending on the characters returns the appropriate token /// Scans a slash '/' and depending on the characters returns the appropriate token
Token scanSlash(); Token scanSlash();

View File

@ -73,6 +73,7 @@ bool DocStringAnalyser::visit(EventDefinition const& _event)
void DocStringAnalyser::checkParameters( void DocStringAnalyser::checkParameters(
CallableDeclaration const& _callable, CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation StructurallyDocumentedAnnotation& _annotation
) )
{ {
@ -86,6 +87,7 @@ void DocStringAnalyser::checkParameters(
for (auto i = paramRange.first; i != paramRange.second; ++i) for (auto i = paramRange.first; i != paramRange.second; ++i)
if (!validParams.count(i->second.paramName)) if (!validParams.count(i->second.paramName))
appendError( appendError(
_node.documentation()->location(),
"Documented parameter \"" + "Documented parameter \"" +
i->second.paramName + i->second.paramName +
"\" not found in the parameter list of the function." "\" not found in the parameter list of the function."
@ -101,7 +103,7 @@ void DocStringAnalyser::handleConstructor(
{ {
static set<string> const validTags = set<string>{"author", "dev", "notice", "param"}; static set<string> const validTags = set<string>{"author", "dev", "notice", "param"};
parseDocStrings(_node, _annotation, validTags, "constructor"); parseDocStrings(_node, _annotation, validTags, "constructor");
checkParameters(_callable, _annotation); checkParameters(_callable, _node, _annotation);
} }
void DocStringAnalyser::handleCallable( void DocStringAnalyser::handleCallable(
@ -112,7 +114,7 @@ void DocStringAnalyser::handleCallable(
{ {
static set<string> const validTags = set<string>{"author", "dev", "notice", "return", "param"}; static set<string> const validTags = set<string>{"author", "dev", "notice", "return", "param"};
parseDocStrings(_node, _annotation, validTags, "functions"); parseDocStrings(_node, _annotation, validTags, "functions");
checkParameters(_callable, _annotation); checkParameters(_callable, _node, _annotation);
} }
void DocStringAnalyser::parseDocStrings( void DocStringAnalyser::parseDocStrings(
@ -134,7 +136,10 @@ void DocStringAnalyser::parseDocStrings(
for (auto const& docTag: _annotation.docTags) for (auto const& docTag: _annotation.docTags)
{ {
if (!_validTags.count(docTag.first)) if (!_validTags.count(docTag.first))
appendError("Documentation tag @" + docTag.first + " not valid for " + _nodeName + "."); appendError(
_node.documentation()->location(),
"Documentation tag @" + docTag.first + " not valid for " + _nodeName + "."
);
else else
if (docTag.first == "return") if (docTag.first == "return")
{ {
@ -145,14 +150,18 @@ void DocStringAnalyser::parseDocStrings(
string firstWord = content.substr(0, content.find_first_of(" \t")); string firstWord = content.substr(0, content.find_first_of(" \t"));
if (returnTagsVisited > function->returnParameters().size()) if (returnTagsVisited > function->returnParameters().size())
appendError("Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + appendError(
_node.documentation()->location(),
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
" exceedes the number of return parameters." " exceedes the number of return parameters."
); );
else else
{ {
auto parameter = function->returnParameters().at(returnTagsVisited - 1); auto parameter = function->returnParameters().at(returnTagsVisited - 1);
if (!parameter->name().empty() && parameter->name() != firstWord) if (!parameter->name().empty() && parameter->name() != firstWord)
appendError("Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + appendError(
_node.documentation()->location(),
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
" does not contain the name of its return parameter." " does not contain the name of its return parameter."
); );
} }
@ -161,8 +170,8 @@ void DocStringAnalyser::parseDocStrings(
} }
} }
void DocStringAnalyser::appendError(string const& _description) void DocStringAnalyser::appendError(SourceLocation const& _location, string const& _description)
{ {
m_errorOccured = true; m_errorOccured = true;
m_errorReporter.docstringParsingError(_description); m_errorReporter.docstringParsingError(_location, _description);
} }

View File

@ -51,6 +51,7 @@ private:
void checkParameters( void checkParameters(
CallableDeclaration const& _callable, CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation StructurallyDocumentedAnnotation& _annotation
); );
@ -73,7 +74,7 @@ private:
std::string const& _nodeName std::string const& _nodeName
); );
void appendError(std::string const& _description); void appendError(langutil::SourceLocation const& _location, std::string const& _description);
bool m_errorOccured = false; bool m_errorOccured = false;
langutil::ErrorReporter& m_errorReporter; langutil::ErrorReporter& m_errorReporter;

View File

@ -0,0 +1,11 @@
Error: Documentation tag "@return No value returned" does not contain the name of its return parameter.
--> structured_documentation_source_location/input.sol:3:5:
|
3 | /// @param id Some identifier
| ^ (Relevant source part starts here and spans across multiple lines).
Error: Documentation tag "@return No value returned" does not contain the name of its return parameter.
--> structured_documentation_source_location/input.sol:7:5:
|
7 | /// @return No value returned
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1 @@
1

View File

@ -1,7 +1,9 @@
pragma solidity >= 0.0;
abstract contract C { abstract contract C {
/// @param id Some identifier /// @param id Some identifier
/// @return No value returned /// @return No value returned
function vote(uint id) public virtual returns (uint value); function vote(uint id) public virtual returns (uint value);
}
// ---- /// @return No value returned
// DocstringParsingError: Documentation tag "@return No value returned" does not contain the name of its return parameter. function unvote(uint id) public virtual returns (uint value);
}

View File

@ -4,4 +4,4 @@ abstract contract C {
function vote(uint id) public virtual returns (uint value); function vote(uint id) public virtual returns (uint value);
} }
// ---- // ----
// DocstringParsingError: Documentation tag "@return No value returned" does not contain the name of its return parameter. // DocstringParsingError: (26-89): Documentation tag "@return No value returned" does not contain the name of its return parameter.

View File

@ -7,5 +7,5 @@ contract C {
} }
} }
// ---- // ----
// DocstringParsingError: Documented parameter "" not found in the parameter list of the function. // DocstringParsingError: (17-101): Documented parameter "" not found in the parameter list of the function.
// DocstringParsingError: Documented parameter "_" not found in the parameter list of the function. // DocstringParsingError: (17-101): Documented parameter "_" not found in the parameter list of the function.

View File

@ -0,0 +1,11 @@
abstract contract C {
/// @param id Some identifier
/// @return No value returned
function vote(uint id) public virtual returns (uint value);
/// @return No value returned
function unvote(uint id) public virtual returns (uint value);
}
// ----
// DocstringParsingError: (26-89): Documentation tag "@return No value returned" does not contain the name of its return parameter.
// DocstringParsingError: (159-188): Documentation tag "@return No value returned" does not contain the name of its return parameter.