2828#include < liblangutil/ErrorReporter.h>
2929#include < liblangutil/Exceptions.h>
3030#include < liblangutil/Scanner.h>
31+ #include < liblangutil/Common.h>
3132#include < libsolutil/Common.h>
3233#include < libsolutil/Visitor.h>
3334
34- #include < range/v3/view/subrange.hpp>
35-
3635#include < boost/algorithm/string.hpp>
3736
3837#include < algorithm>
@@ -53,7 +52,7 @@ std::optional<int> toInt(std::string const& _value)
5352 {
5453 return stoi (_value);
5554 }
56- catch (... )
55+ catch (std::out_of_range const & )
5756 {
5857 return std::nullopt ;
5958 }
@@ -193,13 +192,41 @@ std::optional<std::pair<std::string_view, SourceLocation>> Parser::parseSrcComme
193192 langutil::SourceLocation const & _commentLocation
194193)
195194{
196- static std::regex const argsRegex = std::regex (
197- R"~~( ^(-1|\d+):(-1|\d+):(-1|\d+)(?:\s+|$))~~" // index and location, e.g.: 1:234:-1
198- R"~~( ("(?:[^"\\]|\\.)*"?)?)~~" , // optional code snippet, e.g.: "string memory s = \"abc\";..."
199- std::regex_constants::ECMAScript | std::regex_constants::optimize
200- );
201- std::match_results<std::string_view::const_iterator> match;
202- if (!regex_search (_arguments.cbegin (), _arguments.cend (), match, argsRegex))
195+ CharStream argumentStream (std::string (_arguments), " " );
196+ Scanner scanner (argumentStream, ScannerKind::SpecialComment);
197+
198+ std::string_view tail{_arguments.substr (_arguments.size ())};
199+ auto const parseLocationComponent = [](Scanner& _scanner, bool expectTrailingColon) -> std::optional<std::string>
200+ {
201+ bool negative = false ;
202+ if (_scanner.currentToken () == Token::Sub)
203+ {
204+ negative = true ;
205+ _scanner.next ();
206+ }
207+ if (_scanner.currentToken () != Token::Number)
208+ return std::nullopt ;
209+ if (expectTrailingColon && _scanner.peekNextToken () != Token::Colon)
210+ return std::nullopt ;
211+ if (!isValidDecimal (_scanner.currentLiteral ()))
212+ return std::nullopt ;
213+ std::string decimal = (negative ? " -" : " " ) + _scanner.currentLiteral ();
214+ _scanner.next ();
215+ if (expectTrailingColon)
216+ _scanner.next ();
217+ return decimal;
218+ };
219+ std::optional<std::string> rawSourceIndex = parseLocationComponent (scanner, true );
220+ std::optional<std::string> rawStart = parseLocationComponent (scanner, true );
221+ std::optional<std::string> rawEnd = parseLocationComponent (scanner, false );
222+
223+ size_t const snippetStart = static_cast <size_t >(scanner.currentLocation ().start );
224+ bool const locationScannedSuccessfully = rawSourceIndex && rawStart && rawEnd;
225+ bool const locationIsWhitespaceSeparated =
226+ scanner.peekNextToken () == Token::EOS ||
227+ (snippetStart > 0 && langutil::isWhiteSpace (_arguments[snippetStart - 1 ]));
228+
229+ if (!locationScannedSuccessfully || !locationIsWhitespaceSeparated)
203230 {
204231 m_errorReporter.syntaxError (
205232 8387_error,
@@ -209,13 +236,16 @@ std::optional<std::pair<std::string_view, SourceLocation>> Parser::parseSrcComme
209236 return std::nullopt ;
210237 }
211238
212- solAssert (match.size () == 5 , " " );
213- std::string_view tail = _arguments.substr (static_cast <size_t >(match.position () + match.length ()));
239+ // captures error cases `"test` (illegal end quote) and `"test\` (illegal escape sequence / dangling backslash)
240+ bool const illegalLiteral = scanner.currentToken () == Token::Illegal && (scanner.currentError () == ScannerError::IllegalStringEndQuote || scanner.currentError () == ScannerError::IllegalEscapeSequence);
241+ if (scanner.currentToken () == Token::StringLiteral || illegalLiteral)
242+ tail = _arguments.substr (static_cast <size_t >(scanner.currentLocation ().end ));
243+ else
244+ tail = _arguments.substr (static_cast <size_t >(scanner.currentLocation ().start ));
214245
215- if (match[4 ].matched && (
216- !boost::algorithm::ends_with (match[4 ].str (), " \" " ) ||
217- boost::algorithm::ends_with (match[4 ].str (), " \\\" " )
218- ))
246+ // Other scanner errors may occur if there is no string literal which follows
247+ // (f.ex. IllegalHexDigit, IllegalCommentTerminator), but these are ignored
248+ if (illegalLiteral)
219249 {
220250 m_errorReporter.syntaxError (
221251 1544_error,
@@ -225,30 +255,34 @@ std::optional<std::pair<std::string_view, SourceLocation>> Parser::parseSrcComme
225255 return {{tail, SourceLocation{}}};
226256 }
227257
228- std::optional<int > const sourceIndex = toInt (match[ 1 ]. str () );
229- std::optional<int > const start = toInt (match[ 2 ]. str () );
230- std::optional<int > const end = toInt (match[ 3 ]. str () );
258+ std::optional<int > const sourceIndex = toInt (*rawSourceIndex );
259+ std::optional<int > const start = toInt (*rawStart );
260+ std::optional<int > const end = toInt (*rawEnd );
231261
232- if (!sourceIndex.has_value () || !start.has_value () || !end.has_value ())
262+ if (
263+ !sourceIndex.has_value () || *sourceIndex < -1 ||
264+ !start.has_value () || *start < -1 ||
265+ !end.has_value () || *end < -1
266+ )
233267 m_errorReporter.syntaxError (
234268 6367_error,
235269 _commentLocation,
236270 " Invalid value in source location mapping. "
237271 " Expected non-negative integer values or -1 for source index and location."
238272 );
239273 else if (sourceIndex == -1 )
240- return {{tail, SourceLocation{start. value (), end. value () , nullptr }}};
241- else if (!(sourceIndex >= 0 && m_sourceNames->count (static_cast <unsigned >(sourceIndex. value () ))))
274+ return {{tail, SourceLocation{* start, * end, nullptr }}};
275+ else if (!(sourceIndex >= 0 && m_sourceNames->count (static_cast <unsigned >(* sourceIndex))))
242276 m_errorReporter.syntaxError (
243277 2674_error,
244278 _commentLocation,
245279 " Invalid source mapping. Source index not defined via @use-src."
246280 );
247281 else
248282 {
249- std::shared_ptr<std::string const > sourceName = m_sourceNames->at (static_cast <unsigned >(sourceIndex. value () ));
283+ std::shared_ptr<std::string const > sourceName = m_sourceNames->at (static_cast <unsigned >(* sourceIndex));
250284 solAssert (sourceName, " " );
251- return {{tail, SourceLocation{start. value (), end. value () , std::move (sourceName)}}};
285+ return {{tail, SourceLocation{* start, * end, std::move (sourceName)}}};
252286 }
253287 return {{tail, SourceLocation{}}};
254288}
0 commit comments