Coverage Report

Created: 2022-08-24 06:40

/src/solidity/libyul/AsmParser.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
/**
19
 * @author Christian <c@ethdev.com>
20
 * @date 2016
21
 * Solidity inline assembly parser.
22
 */
23
24
#include <libyul/AST.h>
25
#include <libyul/AsmParser.h>
26
#include <libyul/Exceptions.h>
27
#include <liblangutil/ErrorReporter.h>
28
#include <liblangutil/Exceptions.h>
29
#include <liblangutil/Scanner.h>
30
#include <libsolutil/Common.h>
31
#include <libsolutil/Visitor.h>
32
33
#include <range/v3/view/subrange.hpp>
34
35
#include <boost/algorithm/string.hpp>
36
37
#include <algorithm>
38
#include <regex>
39
40
using namespace std;
41
using namespace solidity;
42
using namespace solidity::util;
43
using namespace solidity::langutil;
44
using namespace solidity::yul;
45
46
namespace
47
{
48
49
optional<int> toInt(string const& _value)
50
0
{
51
0
  try
52
0
  {
53
0
    return stoi(_value);
54
0
  }
55
0
  catch (...)
56
0
  {
57
0
    return nullopt;
58
0
  }
59
0
}
60
61
}
62
63
std::shared_ptr<DebugData const> Parser::createDebugData() const
64
1.35M
{
65
1.35M
  switch (m_useSourceLocationFrom)
66
1.35M
  {
67
1.35M
    case UseSourceLocationFrom::Scanner:
68
1.35M
      return DebugData::create(ParserBase::currentLocation(), ParserBase::currentLocation());
69
0
    case UseSourceLocationFrom::LocationOverride:
70
0
      return DebugData::create(m_locationOverride, m_locationOverride);
71
0
    case UseSourceLocationFrom::Comments:
72
0
      return DebugData::create(ParserBase::currentLocation(), m_locationFromComment, m_astIDFromComment);
73
1.35M
  }
74
0
  solAssert(false, "");
75
0
}
76
77
void Parser::updateLocationEndFrom(
78
  shared_ptr<DebugData const>& _debugData,
79
  SourceLocation const& _location
80
) const
81
561k
{
82
561k
  solAssert(_debugData, "");
83
84
561k
  switch (m_useSourceLocationFrom)
85
561k
  {
86
561k
    case UseSourceLocationFrom::Scanner:
87
561k
    {
88
561k
      DebugData updatedDebugData = *_debugData;
89
561k
      updatedDebugData.nativeLocation.end = _location.end;
90
561k
      updatedDebugData.originLocation.end = _location.end;
91
561k
      _debugData = make_shared<DebugData const>(move(updatedDebugData));
92
561k
      break;
93
0
    }
94
0
    case UseSourceLocationFrom::LocationOverride:
95
      // Ignore the update. The location we're overriding with is not supposed to change
96
0
      break;
97
0
    case UseSourceLocationFrom::Comments:
98
0
    {
99
0
      DebugData updatedDebugData = *_debugData;
100
0
      updatedDebugData.nativeLocation.end = _location.end;
101
0
      _debugData = make_shared<DebugData const>(move(updatedDebugData));
102
0
      break;
103
0
    }
104
561k
  }
105
561k
}
106
107
unique_ptr<Block> Parser::parse(CharStream& _charStream)
108
0
{
109
0
  m_scanner = make_shared<Scanner>(_charStream);
110
0
  unique_ptr<Block> block = parseInline(m_scanner);
111
0
  expectToken(Token::EOS);
112
0
  return block;
113
0
}
114
115
unique_ptr<Block> Parser::parseInline(std::shared_ptr<Scanner> const& _scanner)
116
19.5k
{
117
19.5k
  m_recursionDepth = 0;
118
119
19.5k
  _scanner->setScannerMode(ScannerKind::Yul);
120
19.5k
  ScopeGuard resetScanner([&]{ _scanner->setScannerMode(ScannerKind::Solidity); });
121
122
19.5k
  try
123
19.5k
  {
124
19.5k
    m_scanner = _scanner;
125
19.5k
    if (m_useSourceLocationFrom == UseSourceLocationFrom::Comments)
126
0
      fetchDebugDataFromComment();
127
19.5k
    return make_unique<Block>(parseBlock());
128
19.5k
  }
129
19.5k
  catch (FatalError const&)
130
19.5k
  {
131
0
    yulAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported.");
132
0
  }
133
134
0
  return nullptr;
135
19.5k
}
136
137
langutil::Token Parser::advance()
138
2.76M
{
139
2.76M
  auto const token = ParserBase::advance();
140
2.76M
  if (m_useSourceLocationFrom == UseSourceLocationFrom::Comments)
141
0
    fetchDebugDataFromComment();
142
2.76M
  return token;
143
2.76M
}
144
145
void Parser::fetchDebugDataFromComment()
146
0
{
147
0
  solAssert(m_sourceNames.has_value(), "");
148
149
0
  static regex const tagRegex = regex(
150
0
    R"~~((?:^|\s+)(@[a-zA-Z0-9\-_]+)(?:\s+|$))~~", // tag, e.g: @src
151
0
    regex_constants::ECMAScript | regex_constants::optimize
152
0
  );
153
154
0
  string_view commentLiteral = m_scanner->currentCommentLiteral();
155
0
  match_results<string_view::const_iterator> match;
156
157
0
  langutil::SourceLocation originLocation = m_locationFromComment;
158
  // Empty for each new node.
159
0
  optional<int> astID;
160
161
0
  while (regex_search(commentLiteral.cbegin(), commentLiteral.cend(), match, tagRegex))
162
0
  {
163
0
    solAssert(match.size() == 2, "");
164
0
    commentLiteral = commentLiteral.substr(static_cast<size_t>(match.position() + match.length()));
165
166
0
    if (match[1] == "@src")
167
0
    {
168
0
      if (auto parseResult = parseSrcComment(commentLiteral, m_scanner->currentCommentLocation()))
169
0
        tie(commentLiteral, originLocation) = *parseResult;
170
0
      else
171
0
        break;
172
0
    }
173
0
    else if (match[1] == "@ast-id")
174
0
    {
175
0
      if (auto parseResult = parseASTIDComment(commentLiteral, m_scanner->currentCommentLocation()))
176
0
        tie(commentLiteral, astID) = *parseResult;
177
0
      else
178
0
        break;
179
0
    }
180
0
    else
181
      // Ignore unrecognized tags.
182
0
      continue;
183
0
  }
184
185
0
  m_locationFromComment = originLocation;
186
0
  m_astIDFromComment = astID;
187
0
}
188
189
optional<pair<string_view, SourceLocation>> Parser::parseSrcComment(
190
  string_view const _arguments,
191
  langutil::SourceLocation const& _commentLocation
192
)
193
0
{
194
0
  static regex const argsRegex = regex(
195
0
    R"~~(^(-1|\d+):(-1|\d+):(-1|\d+)(?:\s+|$))~~"  // index and location, e.g.: 1:234:-1
196
0
    R"~~(("(?:[^"\\]|\\.)*"?)?)~~",                // optional code snippet, e.g.: "string memory s = \"abc\";..."
197
0
    regex_constants::ECMAScript | regex_constants::optimize
198
0
  );
199
0
  match_results<string_view::const_iterator> match;
200
0
  if (!regex_search(_arguments.cbegin(), _arguments.cend(), match, argsRegex))
201
0
  {
202
0
    m_errorReporter.syntaxError(
203
0
      8387_error,
204
0
      _commentLocation,
205
0
      "Invalid values in source location mapping. Could not parse location specification."
206
0
    );
207
0
    return nullopt;
208
0
  }
209
210
0
  solAssert(match.size() == 5, "");
211
0
  string_view tail = _arguments.substr(static_cast<size_t>(match.position() + match.length()));
212
213
0
  if (match[4].matched && (
214
0
    !boost::algorithm::ends_with(match[4].str(), "\"") ||
215
0
    boost::algorithm::ends_with(match[4].str(), "\\\"")
216
0
  ))
217
0
  {
218
0
    m_errorReporter.syntaxError(
219
0
      1544_error,
220
0
      _commentLocation,
221
0
      "Invalid code snippet in source location mapping. Quote is not terminated."
222
0
    );
223
0
    return {{tail, SourceLocation{}}};
224
0
  }
225
226
0
  optional<int> const sourceIndex = toInt(match[1].str());
227
0
  optional<int> const start = toInt(match[2].str());
228
0
  optional<int> const end = toInt(match[3].str());
229
230
0
  if (!sourceIndex.has_value() || !start.has_value() || !end.has_value())
231
0
    m_errorReporter.syntaxError(
232
0
      6367_error,
233
0
      _commentLocation,
234
0
      "Invalid value in source location mapping. "
235
0
      "Expected non-negative integer values or -1 for source index and location."
236
0
    );
237
0
  else if (sourceIndex == -1)
238
0
    return {{tail, SourceLocation{start.value(), end.value(), nullptr}}};
239
0
  else if (!(sourceIndex >= 0 && m_sourceNames->count(static_cast<unsigned>(sourceIndex.value()))))
240
0
    m_errorReporter.syntaxError(
241
0
      2674_error,
242
0
      _commentLocation,
243
0
      "Invalid source mapping. Source index not defined via @use-src."
244
0
    );
245
0
  else
246
0
  {
247
0
    shared_ptr<string const> sourceName = m_sourceNames->at(static_cast<unsigned>(sourceIndex.value()));
248
0
    solAssert(sourceName, "");
249
0
    return {{tail, SourceLocation{start.value(), end.value(), move(sourceName)}}};
250
0
  }
251
0
  return {{tail, SourceLocation{}}};
252
0
}
253
254
optional<pair<string_view, optional<int>>> Parser::parseASTIDComment(
255
  string_view _arguments,
256
  langutil::SourceLocation const& _commentLocation
257
)
258
0
{
259
0
  static regex const argRegex = regex(
260
0
    R"~~(^(\d+)(?:\s|$))~~",
261
0
    regex_constants::ECMAScript | regex_constants::optimize
262
0
  );
263
0
  match_results<string_view::const_iterator> match;
264
0
  optional<int> astID;
265
0
  bool matched = regex_search(_arguments.cbegin(), _arguments.cend(), match, argRegex);
266
0
  string_view tail = _arguments;
267
0
  if (matched)
268
0
  {
269
0
    solAssert(match.size() == 2, "");
270
0
    tail = _arguments.substr(static_cast<size_t>(match.position() + match.length()));
271
272
0
    astID = toInt(match[1].str());
273
0
  }
274
275
0
  if (!matched || !astID || *astID < 0 || static_cast<int64_t>(*astID) != *astID)
276
0
  {
277
0
    m_errorReporter.syntaxError(1749_error, _commentLocation, "Invalid argument for @ast-id.");
278
0
    astID = nullopt;
279
0
  }
280
0
  if (matched)
281
0
    return {{_arguments, astID}};
282
0
  else
283
0
    return nullopt;
284
0
}
285
286
Block Parser::parseBlock()
287
119k
{
288
119k
  RecursionGuard recursionGuard(*this);
289
119k
  Block block = createWithLocation<Block>();
290
119k
  expectToken(Token::LBrace);
291
370k
  while (currentToken() != Token::RBrace)
292
251k
    block.statements.emplace_back(parseStatement());
293
119k
  updateLocationEndFrom(block.debugData, currentLocation());
294
119k
  advance();
295
119k
  return block;
296
119k
}
297
298
Statement Parser::parseStatement()
299
251k
{
300
251k
  RecursionGuard recursionGuard(*this);
301
251k
  switch (currentToken())
302
251k
  {
303
46.2k
  case Token::Let:
304
46.2k
    return parseVariableDeclaration();
305
30.7k
  case Token::Function:
306
30.7k
    return parseFunctionDefinition();
307
22.5k
  case Token::LBrace:
308
22.5k
    return parseBlock();
309
2.26k
  case Token::If:
310
2.26k
  {
311
2.26k
    If _if = createWithLocation<If>();
312
2.26k
    advance();
313
2.26k
    _if.condition = make_unique<Expression>(parseExpression());
314
2.26k
    _if.body = parseBlock();
315
2.26k
    updateLocationEndFrom(_if.debugData, nativeLocationOf(_if.body));
316
2.26k
    return Statement{move(_if)};
317
0
  }
318
7.56k
  case Token::Switch:
319
7.56k
  {
320
7.56k
    Switch _switch = createWithLocation<Switch>();
321
7.56k
    advance();
322
7.56k
    _switch.expression = make_unique<Expression>(parseExpression());
323
25.1k
    while (currentToken() == Token::Case)
324
17.6k
      _switch.cases.emplace_back(parseCase());
325
7.56k
    if (currentToken() == Token::Default)
326
5.85k
      _switch.cases.emplace_back(parseCase());
327
7.56k
    if (currentToken() == Token::Default)
328
0
      fatalParserError(6931_error, "Only one default case allowed.");
329
7.56k
    else if (currentToken() == Token::Case)
330
0
      fatalParserError(4904_error, "Case not allowed after default case.");
331
7.56k
    if (_switch.cases.empty())
332
0
      fatalParserError(2418_error, "Switch statement without any cases.");
333
7.56k
    updateLocationEndFrom(_switch.debugData, nativeLocationOf(_switch.cases.back().body));
334
7.56k
    return Statement{move(_switch)};
335
0
  }
336
6.97k
  case Token::For:
337
6.97k
    return parseForLoop();
338
503
  case Token::Break:
339
503
  {
340
503
    Statement stmt{createWithLocation<Break>()};
341
503
    checkBreakContinuePosition("break");
342
503
    advance();
343
503
    return stmt;
344
0
  }
345
729
  case Token::Continue:
346
729
  {
347
729
    Statement stmt{createWithLocation<Continue>()};
348
729
    checkBreakContinuePosition("continue");
349
729
    advance();
350
729
    return stmt;
351
0
  }
352
1.28k
  case Token::Leave:
353
1.28k
  {
354
1.28k
    Statement stmt{createWithLocation<Leave>()};
355
1.28k
    if (!m_insideFunction)
356
0
      m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function.");
357
1.28k
    advance();
358
1.28k
    return stmt;
359
0
  }
360
132k
  default:
361
132k
    break;
362
251k
  }
363
364
  // Options left:
365
  // Expression/FunctionCall
366
  // Assignment
367
132k
  variant<Literal, Identifier> elementary(parseLiteralOrIdentifier());
368
369
132k
  switch (currentToken())
370
132k
  {
371
119k
  case Token::LParen:
372
119k
  {
373
119k
    Expression expr = parseCall(std::move(elementary));
374
119k
    return ExpressionStatement{debugDataOf(expr), move(expr)};
375
0
  }
376
0
  case Token::Comma:
377
12.2k
  case Token::AssemblyAssign:
378
12.2k
  {
379
12.2k
    Assignment assignment;
380
12.2k
    assignment.debugData = debugDataOf(elementary);
381
382
12.2k
    while (true)
383
12.2k
    {
384
12.2k
      if (!holds_alternative<Identifier>(elementary))
385
0
      {
386
0
        auto const token = currentToken() == Token::Comma ? "," : ":=";
387
388
0
        fatalParserError(
389
0
          2856_error,
390
0
          std::string("Variable name must precede \"") +
391
0
          token +
392
0
          "\"" +
393
0
          (currentToken() == Token::Comma ? " in multiple assignment." : " in assignment.")
394
0
        );
395
0
      }
396
397
12.2k
      auto const& identifier = std::get<Identifier>(elementary);
398
399
12.2k
      if (m_dialect.builtin(identifier.name))
400
0
        fatalParserError(6272_error, "Cannot assign to builtin function \"" + identifier.name.str() + "\".");
401
402
12.2k
      assignment.variableNames.emplace_back(identifier);
403
404
12.2k
      if (currentToken() != Token::Comma)
405
12.2k
        break;
406
407
0
      expectToken(Token::Comma);
408
409
0
      elementary = parseLiteralOrIdentifier();
410
0
    }
411
412
12.2k
    expectToken(Token::AssemblyAssign);
413
414
12.2k
    assignment.value = make_unique<Expression>(parseExpression());
415
12.2k
    updateLocationEndFrom(assignment.debugData, nativeLocationOf(*assignment.value));
416
417
12.2k
    return Statement{move(assignment)};
418
0
  }
419
0
  default:
420
0
    fatalParserError(6913_error, "Call or assignment expected.");
421
0
    break;
422
132k
  }
423
424
0
  yulAssert(false, "");
425
0
  return {};
426
0
}
427
428
Case Parser::parseCase()
429
23.4k
{
430
23.4k
  RecursionGuard recursionGuard(*this);
431
23.4k
  Case _case = createWithLocation<Case>();
432
23.4k
  if (currentToken() == Token::Default)
433
5.85k
    advance();
434
17.6k
  else if (currentToken() == Token::Case)
435
17.6k
  {
436
17.6k
    advance();
437
17.6k
    variant<Literal, Identifier> literal = parseLiteralOrIdentifier();
438
17.6k
    if (!holds_alternative<Literal>(literal))
439
0
      fatalParserError(4805_error, "Literal expected.");
440
17.6k
    _case.value = make_unique<Literal>(std::get<Literal>(std::move(literal)));
441
17.6k
  }
442
0
  else
443
17.6k
    yulAssert(false, "Case or default case expected.");
444
23.4k
  _case.body = parseBlock();
445
23.4k
  updateLocationEndFrom(_case.debugData, nativeLocationOf(_case.body));
446
23.4k
  return _case;
447
23.4k
}
448
449
ForLoop Parser::parseForLoop()
450
6.97k
{
451
6.97k
  RecursionGuard recursionGuard(*this);
452
453
6.97k
  ForLoopComponent outerForLoopComponent = m_currentForLoopComponent;
454
455
6.97k
  ForLoop forLoop = createWithLocation<ForLoop>();
456
6.97k
  expectToken(Token::For);
457
6.97k
  m_currentForLoopComponent = ForLoopComponent::ForLoopPre;
458
6.97k
  forLoop.pre = parseBlock();
459
6.97k
  m_currentForLoopComponent = ForLoopComponent::None;
460
6.97k
  forLoop.condition = make_unique<Expression>(parseExpression());
461
6.97k
  m_currentForLoopComponent = ForLoopComponent::ForLoopPost;
462
6.97k
  forLoop.post = parseBlock();
463
6.97k
  m_currentForLoopComponent = ForLoopComponent::ForLoopBody;
464
6.97k
  forLoop.body = parseBlock();
465
6.97k
  updateLocationEndFrom(forLoop.debugData, nativeLocationOf(forLoop.body));
466
467
6.97k
  m_currentForLoopComponent = outerForLoopComponent;
468
469
6.97k
  return forLoop;
470
6.97k
}
471
472
Expression Parser::parseExpression()
473
681k
{
474
681k
  RecursionGuard recursionGuard(*this);
475
476
681k
  variant<Literal, Identifier> operation = parseLiteralOrIdentifier();
477
681k
  return visit(GenericVisitor{
478
681k
    [&](Identifier& _identifier) -> Expression
479
681k
    {
480
298k
      if (currentToken() == Token::LParen)
481
192k
        return parseCall(std::move(operation));
482
105k
      if (m_dialect.builtin(_identifier.name))
483
0
        fatalParserError(
484
0
          7104_error,
485
0
          nativeLocationOf(_identifier),
486
0
          "Builtin function \"" + _identifier.name.str() + "\" must be called."
487
0
        );
488
105k
      return move(_identifier);
489
298k
    },
490
681k
    [&](Literal& _literal) -> Expression
491
681k
    {
492
383k
      return move(_literal);
493
383k
    }
494
681k
  }, operation);
495
681k
}
496
497
variant<Literal, Identifier> Parser::parseLiteralOrIdentifier()
498
831k
{
499
831k
  RecursionGuard recursionGuard(*this);
500
831k
  switch (currentToken())
501
831k
  {
502
430k
  case Token::Identifier:
503
430k
  {
504
430k
    Identifier identifier{createDebugData(), YulString{currentLiteral()}};
505
430k
    advance();
506
430k
    return identifier;
507
0
  }
508
4.84k
  case Token::StringLiteral:
509
4.84k
  case Token::HexStringLiteral:
510
398k
  case Token::Number:
511
400k
  case Token::TrueLiteral:
512
400k
  case Token::FalseLiteral:
513
400k
  {
514
400k
    LiteralKind kind = LiteralKind::Number;
515
400k
    switch (currentToken())
516
400k
    {
517
4.84k
    case Token::StringLiteral:
518
4.84k
    case Token::HexStringLiteral:
519
4.84k
      kind = LiteralKind::String;
520
4.84k
      break;
521
393k
    case Token::Number:
522
393k
      if (!isValidNumberLiteral(currentLiteral()))
523
0
        fatalParserError(4828_error, "Invalid number literal.");
524
393k
      kind = LiteralKind::Number;
525
393k
      break;
526
2.22k
    case Token::TrueLiteral:
527
2.45k
    case Token::FalseLiteral:
528
2.45k
      kind = LiteralKind::Boolean;
529
2.45k
      break;
530
0
    default:
531
0
      break;
532
400k
    }
533
534
400k
    Literal literal{
535
400k
      createDebugData(),
536
400k
      kind,
537
400k
      YulString{currentLiteral()},
538
400k
      kind == LiteralKind::Boolean ? m_dialect.boolType : m_dialect.defaultType
539
400k
    };
540
400k
    advance();
541
400k
    if (currentToken() == Token::Colon)
542
0
    {
543
0
      expectToken(Token::Colon);
544
0
      updateLocationEndFrom(literal.debugData, currentLocation());
545
0
      literal.type = expectAsmIdentifier();
546
0
    }
547
548
400k
    return literal;
549
400k
  }
550
0
  case Token::Illegal:
551
0
    fatalParserError(1465_error, "Illegal token: " + to_string(m_scanner->currentError()));
552
0
    break;
553
0
  default:
554
0
    fatalParserError(1856_error, "Literal or identifier expected.");
555
831k
  }
556
0
  return {};
557
831k
}
558
559
VariableDeclaration Parser::parseVariableDeclaration()
560
46.2k
{
561
46.2k
  RecursionGuard recursionGuard(*this);
562
46.2k
  VariableDeclaration varDecl = createWithLocation<VariableDeclaration>();
563
46.2k
  expectToken(Token::Let);
564
121k
  while (true)
565
121k
  {
566
121k
    varDecl.variables.emplace_back(parseTypedName());
567
121k
    if (currentToken() == Token::Comma)
568
75.0k
      expectToken(Token::Comma);
569
46.2k
    else
570
46.2k
      break;
571
121k
  }
572
46.2k
  if (currentToken() == Token::AssemblyAssign)
573
41.6k
  {
574
41.6k
    expectToken(Token::AssemblyAssign);
575
41.6k
    varDecl.value = make_unique<Expression>(parseExpression());
576
41.6k
    updateLocationEndFrom(varDecl.debugData, nativeLocationOf(*varDecl.value));
577
41.6k
  }
578
4.66k
  else
579
4.66k
    updateLocationEndFrom(varDecl.debugData, nativeLocationOf(varDecl.variables.back()));
580
581
46.2k
  return varDecl;
582
46.2k
}
583
584
FunctionDefinition Parser::parseFunctionDefinition()
585
30.7k
{
586
30.7k
  RecursionGuard recursionGuard(*this);
587
588
30.7k
  if (m_currentForLoopComponent == ForLoopComponent::ForLoopPre)
589
0
    m_errorReporter.syntaxError(
590
0
      3441_error,
591
0
      currentLocation(),
592
0
      "Functions cannot be defined inside a for-loop init block."
593
0
    );
594
595
30.7k
  ForLoopComponent outerForLoopComponent = m_currentForLoopComponent;
596
30.7k
  m_currentForLoopComponent = ForLoopComponent::None;
597
598
30.7k
  FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
599
30.7k
  expectToken(Token::Function);
600
30.7k
  funDef.name = expectAsmIdentifier();
601
30.7k
  expectToken(Token::LParen);
602
81.8k
  while (currentToken() != Token::RParen)
603
78.4k
  {
604
78.4k
    funDef.parameters.emplace_back(parseTypedName());
605
78.4k
    if (currentToken() == Token::RParen)
606
27.2k
      break;
607
51.1k
    expectToken(Token::Comma);
608
51.1k
  }
609
30.7k
  expectToken(Token::RParen);
610
30.7k
  if (currentToken() == Token::RightArrow)
611
24.8k
  {
612
24.8k
    expectToken(Token::RightArrow);
613
81.0k
    while (true)
614
81.0k
    {
615
81.0k
      funDef.returnVariables.emplace_back(parseTypedName());
616
81.0k
      if (currentToken() == Token::LBrace)
617
24.8k
        break;
618
56.2k
      expectToken(Token::Comma);
619
56.2k
    }
620
24.8k
  }
621
30.7k
  bool preInsideFunction = m_insideFunction;
622
30.7k
  m_insideFunction = true;
623
30.7k
  funDef.body = parseBlock();
624
30.7k
  m_insideFunction = preInsideFunction;
625
30.7k
  updateLocationEndFrom(funDef.debugData, nativeLocationOf(funDef.body));
626
627
30.7k
  m_currentForLoopComponent = outerForLoopComponent;
628
30.7k
  return funDef;
629
30.7k
}
630
631
FunctionCall Parser::parseCall(variant<Literal, Identifier>&& _initialOp)
632
312k
{
633
312k
  RecursionGuard recursionGuard(*this);
634
635
312k
  if (!holds_alternative<Identifier>(_initialOp))
636
0
    fatalParserError(9980_error, "Function name expected.");
637
638
312k
  FunctionCall ret;
639
312k
  ret.functionName = std::move(std::get<Identifier>(_initialOp));
640
312k
  ret.debugData = ret.functionName.debugData;
641
642
312k
  expectToken(Token::LParen);
643
312k
  if (currentToken() != Token::RParen)
644
297k
  {
645
297k
    ret.arguments.emplace_back(parseExpression());
646
610k
    while (currentToken() != Token::RParen)
647
313k
    {
648
313k
      expectToken(Token::Comma);
649
313k
      ret.arguments.emplace_back(parseExpression());
650
313k
    }
651
297k
  }
652
312k
  updateLocationEndFrom(ret.debugData, currentLocation());
653
312k
  expectToken(Token::RParen);
654
312k
  return ret;
655
312k
}
656
657
TypedName Parser::parseTypedName()
658
280k
{
659
280k
  RecursionGuard recursionGuard(*this);
660
280k
  TypedName typedName = createWithLocation<TypedName>();
661
280k
  typedName.name = expectAsmIdentifier();
662
280k
  if (currentToken() == Token::Colon)
663
0
  {
664
0
    expectToken(Token::Colon);
665
0
    updateLocationEndFrom(typedName.debugData, currentLocation());
666
0
    typedName.type = expectAsmIdentifier();
667
0
  }
668
280k
  else
669
280k
    typedName.type = m_dialect.defaultType;
670
671
280k
  return typedName;
672
280k
}
673
674
YulString Parser::expectAsmIdentifier()
675
311k
{
676
311k
  YulString name{currentLiteral()};
677
311k
  if (currentToken() == Token::Identifier && m_dialect.builtin(name))
678
0
    fatalParserError(5568_error, "Cannot use builtin function name \"" + name.str() + "\" as identifier name.");
679
  // NOTE: We keep the expectation here to ensure the correct source location for the error above.
680
311k
  expectToken(Token::Identifier);
681
311k
  return name;
682
311k
}
683
684
void Parser::checkBreakContinuePosition(string const& _which)
685
1.23k
{
686
1.23k
  switch (m_currentForLoopComponent)
687
1.23k
  {
688
0
  case ForLoopComponent::None:
689
0
    m_errorReporter.syntaxError(2592_error, currentLocation(), "Keyword \"" + _which + "\" needs to be inside a for-loop body.");
690
0
    break;
691
0
  case ForLoopComponent::ForLoopPre:
692
0
    m_errorReporter.syntaxError(9615_error, currentLocation(), "Keyword \"" + _which + "\" in for-loop init block is not allowed.");
693
0
    break;
694
0
  case ForLoopComponent::ForLoopPost:
695
0
    m_errorReporter.syntaxError(2461_error, currentLocation(), "Keyword \"" + _which + "\" in for-loop post block is not allowed.");
696
0
    break;
697
1.23k
  case ForLoopComponent::ForLoopBody:
698
1.23k
    break;
699
1.23k
  }
700
1.23k
}
701
702
bool Parser::isValidNumberLiteral(string const& _literal)
703
393k
{
704
393k
  try
705
393k
  {
706
    // Try to convert _literal to u256.
707
393k
    [[maybe_unused]] auto tmp = u256(_literal);
708
393k
  }
709
393k
  catch (...)
710
393k
  {
711
0
    return false;
712
0
  }
713
393k
  if (boost::starts_with(_literal, "0x"))
714
243k
    return true;
715
149k
  else
716
149k
    return _literal.find_first_not_of("0123456789") == string::npos;
717
393k
}