Browse Source

Scanner properly scans multiline natspec comments

- Single and multiline natspect comments get the initial whitespace
  skipped now

- Some rules introduced for the multiline comments. If first line is
  empty then no newline is added to the literal. Same thing with the
  last line. Finally in all lines initial '*' are skipped
cl-refactor
Lefteris Karapetsas 10 years ago
parent
commit
61a1f4436c
  1. 36
      libsolidity/Scanner.cpp
  2. 2
      libsolidity/Scanner.h
  3. 25
      test/solidityScanner.cpp

36
libsolidity/Scanner.cpp

@ -209,6 +209,15 @@ bool Scanner::skipWhitespace()
return getSourcePos() != startPosition; return getSourcePos() != startPosition;
} }
bool Scanner::skipWhitespaceExceptLF()
{
int const startPosition = getSourcePos();
while (m_char == ' ' || m_char == '\t')
advance();
// Return whether or not we skipped any characters.
return getSourcePos() != startPosition;
}
Token::Value Scanner::skipSingleLineComment() Token::Value Scanner::skipSingleLineComment()
{ {
// The line terminator at the end of the line is not considered // The line terminator at the end of the line is not considered
@ -222,7 +231,8 @@ Token::Value Scanner::skipSingleLineComment()
Token::Value Scanner::scanSingleLineDocComment() Token::Value Scanner::scanSingleLineDocComment()
{ {
LiteralScope literal(this, LITERAL_TYPE_COMMENT); LiteralScope literal(this, LITERAL_TYPE_COMMENT);
advance(); //consume the last '/' advance(); //consume the last '/' at ///
skipWhitespaceExceptLF();
while (!isSourcePastEndOfInput()) while (!isSourcePastEndOfInput())
{ {
if (isLineTerminator(m_char)) if (isLineTerminator(m_char))
@ -273,18 +283,27 @@ Token::Value Scanner::scanMultiLineDocComment()
{ {
LiteralScope literal(this, LITERAL_TYPE_COMMENT); LiteralScope literal(this, LITERAL_TYPE_COMMENT);
bool endFound = false; bool endFound = false;
bool charsAdded = false;
advance(); //consume the last '*' advance(); //consume the last '*' at /**
skipWhitespaceExceptLF();
while (!isSourcePastEndOfInput()) while (!isSourcePastEndOfInput())
{ {
// skip starting '*' in multiine comments //handle newlines in multline comments
if (isLineTerminator(m_char)) if (isLineTerminator(m_char))
{ {
skipWhitespace(); skipWhitespace();
if (!m_source.isPastEndOfInput(2) && m_source.get(1) == '*' && m_source.get(2) != '/') if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) != '/')
{ { // skip first '*' in subsequent lines
addCommentLiteralChar('\n'); if (charsAdded)
m_char = m_source.advanceAndGet(3); addCommentLiteralChar('\n');
m_char = m_source.advanceAndGet(2);
}
else if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/')
{ // if after newline the comment ends, don't insert the newline
m_char = m_source.advanceAndGet(2);
endFound = true;
break;
} }
else else
addCommentLiteralChar('\n'); addCommentLiteralChar('\n');
@ -292,11 +311,12 @@ Token::Value Scanner::scanMultiLineDocComment()
if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/') if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/')
{ {
m_source.advanceAndGet(2); m_char = m_source.advanceAndGet(2);
endFound = true; endFound = true;
break; break;
} }
addCommentLiteralChar(m_char); addCommentLiteralChar(m_char);
charsAdded = true;
advance(); advance();
} }
literal.complete(); literal.complete();

2
libsolidity/Scanner.h

@ -182,6 +182,8 @@ private:
/// Skips all whitespace and @returns true if something was skipped. /// Skips all whitespace and @returns true if something was skipped.
bool skipWhitespace(); bool skipWhitespace();
/// Skips all whitespace except Line feeds and returns true if something was skipped
bool skipWhitespaceExceptLF();
Token::Value skipSingleLineComment(); Token::Value skipSingleLineComment();
Token::Value skipMultiLineComment(); Token::Value skipMultiLineComment();

25
test/solidityScanner.cpp

@ -157,14 +157,14 @@ BOOST_AUTO_TEST_CASE(documentation_comments_parsed_begin)
{ {
Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user")); Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS);
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
} }
BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed_begin) BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed_begin)
{ {
Scanner scanner(CharStream("/** Send $(value / 1000) chocolates to the user*/")); Scanner scanner(CharStream("/** Send $(value / 1000) chocolates to the user*/"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS);
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
} }
BOOST_AUTO_TEST_CASE(documentation_comments_parsed) BOOST_AUTO_TEST_CASE(documentation_comments_parsed)
@ -174,7 +174,19 @@ BOOST_AUTO_TEST_CASE(documentation_comments_parsed)
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
}
BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed)
{
Scanner scanner(CharStream("some other tokens /**\n"
"* Send $(value / 1000) chocolates to the user\n"
"*/"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
} }
BOOST_AUTO_TEST_CASE(comment_before_eos) BOOST_AUTO_TEST_CASE(comment_before_eos)
@ -191,6 +203,13 @@ BOOST_AUTO_TEST_CASE(documentation_comment_before_eos)
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), ""); BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "");
} }
BOOST_AUTO_TEST_CASE(empty_multiline_documentation_comment_before_eos)
{
Scanner scanner(CharStream("/***/"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS);
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "");
}
BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence) BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence)
{ {
Scanner scanner(CharStream("hello_world ///documentation comment \n" Scanner scanner(CharStream("hello_world ///documentation comment \n"

Loading…
Cancel
Save