• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polserver / polserver / 13378462461

17 Feb 2025 08:59PM UTC coverage: 58.754% (+0.3%) from 58.475%
13378462461

push

github

web-flow
Add support for sequence and index bindings (#760)

* update grammar

* add ast nodes; ast building

* Rename to unpacking

* semantic analysis

* executor work part 1, unpacking indices

* renamings; implement index binding

* small cleanup

* use multi_index; more tests

* use multi_index only for rest, otherwise list

* initial formatting

* add missing token decoding

* address self-review comments

* formatting tweaks

* add test for var binding in classes

* add StringIterator

* fix spread tests

* add cfgfile iterator; add cfgelem opersubscript; tests

* add iterator for SQLResultSet and SQLRow; tests

* Copy value in take global/local

* Allow any iterable can index rest unpacking
Always use dictionary as rest object in index unpacking

* add docs, core-changes, doc tests

* formatting changes...

* address self-review comments

* update formatter, format all binding test srcs

* address review comments
- unset var scope

* add cfgfile/cfgelem docs

* reformat objref svg

501 of 550 new or added lines in 19 files covered. (91.09%)

14 existing lines in 3 files now uncovered.

42235 of 71885 relevant lines covered (58.75%)

377989.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.47
/pol-core/bscript/compiler/analyzer/SemanticAnalyzer.cpp
1
#include "SemanticAnalyzer.h"
2

3
#include <boost/range/adaptor/reversed.hpp>
4
#include <list>
5
#include <set>
6

7
#include "bscript/compiler/Report.h"
8
#include "bscript/compiler/analyzer/Constants.h"
9
#include "bscript/compiler/analyzer/FlowControlScope.h"
10
#include "bscript/compiler/analyzer/FunctionVariableScope.h"
11
#include "bscript/compiler/analyzer/LocalVariableScope.h"
12
#include "bscript/compiler/analyzer/LocalVariableScopes.h"
13
#include "bscript/compiler/ast/Argument.h"
14
#include "bscript/compiler/ast/BasicForLoop.h"
15
#include "bscript/compiler/ast/BinaryOperator.h"
16
#include "bscript/compiler/ast/Block.h"
17
#include "bscript/compiler/ast/CaseDispatchDefaultSelector.h"
18
#include "bscript/compiler/ast/CaseDispatchGroup.h"
19
#include "bscript/compiler/ast/CaseDispatchGroups.h"
20
#include "bscript/compiler/ast/CaseDispatchSelectors.h"
21
#include "bscript/compiler/ast/CaseStatement.h"
22
#include "bscript/compiler/ast/ClassBody.h"
23
#include "bscript/compiler/ast/ClassDeclaration.h"
24
#include "bscript/compiler/ast/ClassInstance.h"
25
#include "bscript/compiler/ast/ClassParameterDeclaration.h"
26
#include "bscript/compiler/ast/ConstDeclaration.h"
27
#include "bscript/compiler/ast/CstyleForLoop.h"
28
#include "bscript/compiler/ast/DoWhileLoop.h"
29
#include "bscript/compiler/ast/ForeachLoop.h"
30
#include "bscript/compiler/ast/FunctionBody.h"
31
#include "bscript/compiler/ast/FunctionCall.h"
32
#include "bscript/compiler/ast/FunctionExpression.h"
33
#include "bscript/compiler/ast/FunctionParameterDeclaration.h"
34
#include "bscript/compiler/ast/FunctionParameterList.h"
35
#include "bscript/compiler/ast/FunctionReference.h"
36
#include "bscript/compiler/ast/GeneratedFunction.h"
37
#include "bscript/compiler/ast/Identifier.h"
38
#include "bscript/compiler/ast/IndexBinding.h"
39
#include "bscript/compiler/ast/IntegerValue.h"
40
#include "bscript/compiler/ast/JumpStatement.h"
41
#include "bscript/compiler/ast/MemberAccess.h"
42
#include "bscript/compiler/ast/ModuleFunctionDeclaration.h"
43
#include "bscript/compiler/ast/Program.h"
44
#include "bscript/compiler/ast/ProgramParameterDeclaration.h"
45
#include "bscript/compiler/ast/RepeatUntilLoop.h"
46
#include "bscript/compiler/ast/ReturnStatement.h"
47
#include "bscript/compiler/ast/SequenceBinding.h"
48
#include "bscript/compiler/ast/StringValue.h"
49
#include "bscript/compiler/ast/TopLevelStatements.h"
50
#include "bscript/compiler/ast/UserFunction.h"
51
#include "bscript/compiler/ast/VarStatement.h"
52
#include "bscript/compiler/ast/VariableAssignmentStatement.h"
53
#include "bscript/compiler/ast/VariableBinding.h"
54
#include "bscript/compiler/ast/WhileLoop.h"
55
#include "bscript/compiler/astbuilder/SimpleValueCloner.h"
56
#include "bscript/compiler/model/ClassLink.h"
57
#include "bscript/compiler/model/CompilerWorkspace.h"
58
#include "bscript/compiler/model/FunctionLink.h"
59
#include "bscript/compiler/model/Variable.h"
60
#include "bscript/compiler/optimizer/ConstantValidator.h"
61
#include "clib/strutil.h"
62
#include "filefmt.h"
63

64
namespace Pol::Bscript::Compiler
65
{
66
SemanticAnalyzer::SemanticAnalyzer( CompilerWorkspace& workspace, Report& report )
1,526✔
67
    : workspace( workspace ),
1,526✔
68
      report( report ),
1,526✔
69
      globals( VariableScope::Global, report ),
1,526✔
70
      locals( VariableScope::Local, report ),
1,526✔
71
      captures( VariableScope::Capture, report ),
1,526✔
72
      break_scopes( locals, report ),
1,526✔
73
      continue_scopes( locals, report ),
1,526✔
74
      local_scopes( locals, report ),
1,526✔
75
      capture_scopes( captures, report )
3,052✔
76
{
77
  current_scope_names.push( ScopeName::Global );
1,526✔
78
}
1,526✔
79

80
SemanticAnalyzer::~SemanticAnalyzer() = default;
1,526✔
81

82
void SemanticAnalyzer::register_const_declarations( CompilerWorkspace& workspace, Report& report )
1,538✔
83
{
84
  for ( auto& constant : workspace.const_declarations )
115,533✔
85
  {
86
    report_function_name_conflict( workspace, report, constant->source_location,
227,990✔
87
                                   constant->identifier, "constant" );
113,995✔
88
    workspace.constants.create( *constant );
113,995✔
89
  }
90
}
1,538✔
91

92
void SemanticAnalyzer::analyze()
1,526✔
93
{
94
  workspace.top_level_statements->accept( *this );
1,526✔
95
  if ( auto& program = workspace.program )
1,526✔
96
  {
97
    program->accept( *this );
353✔
98
  }
99

100
  for ( auto& user_function : workspace.user_functions )
3,638✔
101
  {
102
    // Function expressions are analyzed within visit_function_expression.
103
    if ( !user_function->expression )
2,112✔
104
    {
105
      user_function->accept( *this );
1,890✔
106
    }
107
  }
108

109
  // Class declarations do not have var statements as children, so we do not get
110
  // 'duplicate variable' errors.
111
  for ( auto& class_decl : workspace.class_declarations )
1,860✔
112
  {
113
    class_decl->accept( *this );
334✔
114
  }
115

116
  workspace.global_variable_names = globals.get_names();
1,526✔
117
}
1,526✔
118

119
void SemanticAnalyzer::visit_basic_for_loop( BasicForLoop& node )
56✔
120
{
121
  if ( locals.find( node.identifier ) )
56✔
122
  {
123
    report.error( node, "FOR iterator '{}' hides a local variable.", node.identifier );
2✔
124
    return;
4✔
125
  }
126
  if ( report_function_name_conflict( node.source_location, node.identifier, "for loop iterator" ) )
54✔
127
  {
128
    return;
2✔
129
  }
130

131
  node.first().accept( *this );
52✔
132
  node.last().accept( *this );
52✔
133

134
  LocalVariableScope scope( local_scopes, node.local_variable_scope_info );
52✔
135
  scope.create( node.identifier, WarnOn::Never, node.source_location );
52✔
136
  scope.create( "_" + node.identifier + "_end", WarnOn::Never, node.source_location );
52✔
137

138
  FlowControlScope break_scope( break_scopes, node.source_location, node.get_label(),
52✔
139
                                node.break_label );
104✔
140
  FlowControlScope continue_scope( continue_scopes, node.source_location, node.get_label(),
52✔
141
                                   node.continue_label );
104✔
142

143
  node.block().accept( *this );
52✔
144
}
52✔
145

146
void SemanticAnalyzer::visit_block( Block& block )
4,468✔
147
{
148
  LocalVariableScope scope( local_scopes, block.local_variable_scope_info );
4,468✔
149

150
  visit_children( block );
4,468✔
151
}
4,468✔
152

153
void SemanticAnalyzer::visit_index_binding( IndexBinding& node )
54✔
154
{
155
  u8 index = 0;
54✔
156

157
  if ( node.binding_count() > 127 )
54✔
158
  {
159
    report.error( node, "Too many binding elements. Maximum is 127." );
2✔
160
  }
161

162
  for ( const auto& child : node.bindings() )
438✔
163
  {
164
    if ( auto member_binding = dynamic_cast<VariableBinding*>( &child.get() ) )
384✔
165
    {
166
      if ( member_binding->rest )
366✔
167
      {
168
        if ( node.children.back().get() != member_binding )
20✔
169
          report.error( *member_binding, "Index rest binding must be the last in the list." );
2✔
170

171
        node.rest_index = index;
20✔
172
      }
173
    }
174

175
    ++index;
384✔
176
  }
54✔
177

178
  visit_children( node );
54✔
179
}
54✔
180

181
void SemanticAnalyzer::visit_class_declaration( ClassDeclaration& node )
334✔
182
{
183
  const auto& class_name = node.name;
334✔
184
  std::set<std::string, Clib::ci_cmp_pred> named_baseclasses;
334✔
185
  std::list<ClassDeclaration*> to_visit;
334✔
186
  std::set<ClassDeclaration*> visited;
334✔
187

188
  report.debug( node, "Class '{}' declared with {} parameters", class_name,
668✔
189
                node.parameters().size() );
334✔
190

191
  for ( const auto& base_class_link : node.base_class_links )
558✔
192
  {
193
    const auto& baseclass_name = base_class_link->name;
224✔
194
    auto itr = workspace.all_class_locations.find( baseclass_name );
224✔
195
    if ( itr == workspace.all_class_locations.end() )
224✔
196
    {
197
      report.error( base_class_link->source_location,
2✔
198
                    "Class '{}' references unknown base class '{}'", class_name, baseclass_name );
199
    }
200
    else
201
    {
202
      if ( auto cd = base_class_link->class_declaration() )
222✔
203
        to_visit.push_back( cd );
222✔
204
      else
205
        node.internal_error( "no class linked for base class" );
×
206
    }
207

208
    if ( Clib::caseInsensitiveEqual( baseclass_name, class_name ) )
224✔
209
    {
210
      report.error( base_class_link->source_location,
2✔
211
                    "Class '{}' references itself as a base class.", class_name );
212
    }
213

214
    bool previously_referenced =
215
        named_baseclasses.find( baseclass_name ) != named_baseclasses.end();
224✔
216

217
    if ( previously_referenced )
224✔
218
    {
219
      report.error( base_class_link->source_location,
2✔
220
                    "Class '{}' references base class '{}' multiple times.", class_name,
221
                    baseclass_name );
222
    }
223
    else
224
    {
225
      named_baseclasses.emplace( baseclass_name );
222✔
226
      report.debug( base_class_link->source_location, "Class '{}' references base class '{}'",
222✔
227
                    class_name, baseclass_name );
228
    }
229
  }
230

231
  for ( auto to_visit_itr = to_visit.begin(); to_visit_itr != to_visit.end();
636✔
232
        to_visit_itr = to_visit.erase( to_visit_itr ) )
302✔
233
  {
234
    auto cd = *to_visit_itr;
302✔
235
    if ( visited.find( cd ) != visited.end() )
302✔
236
    {
237
      continue;
42✔
238
    }
239

240
    visited.insert( cd );
260✔
241

242
    if ( cd == &node )
260✔
243
    {
244
      report.error( node, "Class '{}' references itself as a base class through inheritance.",
8✔
245
                    class_name );
246
    }
247

248
    for ( const auto& base_class_link : cd->base_class_links )
340✔
249
    {
250
      if ( auto base_cd = base_class_link->class_declaration() )
80✔
251
      {
252
        to_visit.push_back( base_cd );
80✔
253
      }
254
      else
255
      {
256
        cd->internal_error( "no class linked for base class" );
×
257
      }
258
    }
259
  }
260
}
334✔
261

262
class CaseDispatchDuplicateSelectorAnalyzer : public NodeVisitor
263
{
264
public:
265
  explicit CaseDispatchDuplicateSelectorAnalyzer( Report& report ) : report( report ) {}
78✔
266

267
  void visit_block( Block& ) override
240✔
268
  {
269
    // just don't recurse into children
270
  }
240✔
271

272
  void visit_integer_value( IntegerValue& node ) override
106✔
273
  {
274
    auto seen = already_seen_integers.find( node.value );
106✔
275
    if ( seen != already_seen_integers.end() )
106✔
276
    {
277
      report.error( node,
4✔
278
                    "case statement already has a selector for integer value {}.\n"
279
                    "  See also: {}",
280
                    node.value, ( *seen ).second->source_location );
2✔
281
    }
282
    else
283
    {
284
      already_seen_integers[node.value] = &node;
104✔
285
    }
286
  }
106✔
287

288
  void visit_string_value( StringValue& node ) override
74✔
289
  {
290
    auto seen = already_seen_strings.find( node.value );
74✔
291
    if ( seen != already_seen_strings.end() )
74✔
292
    {
293
      report.error( node,
2✔
294
                    "case statement already has a selector for string value {}.\n"
295
                    "  See also: {}",
296
                    Clib::getencodedquotedstring( node.value ), ( *seen ).second->source_location );
4✔
297
    }
298
    else
299
    {
300
      already_seen_strings[node.value] = &node;
72✔
301
    }
302
  }
74✔
303

304
  void visit_case_dispatch_default_selector( CaseDispatchDefaultSelector& node ) override
44✔
305
  {
306
    if ( already_seen_default )
44✔
307
    {
308
      report.error( node,
2✔
309
                    "case statement already has a default clause.\n"
310
                    "  See also: {}",
311
                    already_seen_default->source_location );
2✔
312
    }
313
    else
314
    {
315
      already_seen_default = &node;
42✔
316
    }
317
  }
44✔
318

319
private:
320
  Report& report;
321

322
  CaseDispatchDefaultSelector* already_seen_default = nullptr;
323
  std::map<int, IntegerValue*> already_seen_integers;
324
  std::map<std::string, StringValue*> already_seen_strings;
325
};
326

327

328
void SemanticAnalyzer::visit_case_statement( CaseStatement& case_ast )
78✔
329
{
330
  CaseDispatchDuplicateSelectorAnalyzer duplicate_detector( report );
78✔
331
  case_ast.dispatch_groups().accept( duplicate_detector );
78✔
332

333
  FlowControlScope break_scope( break_scopes, case_ast.source_location, case_ast.get_label(),
78✔
334
                                case_ast.break_label );
156✔
335

336
  visit_children( case_ast );
78✔
337
}
78✔
338

339
void SemanticAnalyzer::visit_case_dispatch_group( CaseDispatchGroup& dispatch_group )
240✔
340
{
341
  FlowControlScope break_scope( break_scopes, dispatch_group.source_location, "",
240✔
342
                                dispatch_group.break_label );
240✔
343

344
  visit_children( dispatch_group );
240✔
345
}
240✔
346

347
class CaseDispatchSelectorAnalyzer : public NodeVisitor
348
{
349
public:
350
  explicit CaseDispatchSelectorAnalyzer( Report& report ) : report( report ) {}
240✔
351

352
  void visit_identifier( Identifier& identifier ) override
×
353
  {
354
    report.error( identifier, "Case selector '{}' is not a constant.", identifier.name() );
×
355
  }
×
356

357
  void visit_string_value( StringValue& sv ) override
74✔
358
  {
359
    if ( sv.value.size() >= 254 )
74✔
360
    {
361
      report.error( sv, "String expressions in CASE statements must be <= 253 characters." );
×
362
    }
363
  }
74✔
364

365
private:
366
  Report& report;
367
};
368

369
void SemanticAnalyzer::visit_case_dispatch_selectors( CaseDispatchSelectors& selectors )
240✔
370
{
371
  visit_children( selectors );
240✔
372

373
  CaseDispatchSelectorAnalyzer selector_analyzer( report );
240✔
374
  selectors.accept( selector_analyzer );
240✔
375
}
240✔
376

377
void SemanticAnalyzer::visit_cstyle_for_loop( CstyleForLoop& loop )
44✔
378
{
379
  visit_loop_statement( loop );
44✔
380
}
44✔
381

382
void SemanticAnalyzer::visit_do_while_loop( DoWhileLoop& do_while )
28✔
383
{
384
  visit_loop_statement( do_while );
28✔
385
}
28✔
386

387
void SemanticAnalyzer::visit_foreach_loop( ForeachLoop& node )
352✔
388
{
389
  if ( report_function_name_conflict( node.source_location, node.iterator_name,
352✔
390
                                      "foreach iterator" ) )
391
  {
392
    return;
2✔
393
  }
394

395
  node.expression().accept( *this );
350✔
396

397
  LocalVariableScope scope( local_scopes, node.local_variable_scope_info );
350✔
398
  scope.create( node.iterator_name, WarnOn::Never, node.source_location );
350✔
399
  scope.create( "_" + node.iterator_name + "_expr", WarnOn::Never, node.source_location );
350✔
400
  scope.create( "_" + node.iterator_name + "_iter", WarnOn::Never, node.source_location );
350✔
401

402
  FlowControlScope break_scope( break_scopes, node.source_location, node.get_label(),
350✔
403
                                node.break_label );
700✔
404
  FlowControlScope continue_scope( continue_scopes, node.source_location, node.get_label(),
350✔
405
                                   node.continue_label );
700✔
406

407
  node.block().accept( *this );
350✔
408
}
350✔
409

410
ScopeName& SemanticAnalyzer::current_scope_name()
7,768✔
411
{
412
  return current_scope_names.top();
7,768✔
413
}
414

415
void SemanticAnalyzer::visit_function_call( FunctionCall& fc )
12,879✔
416
{
417
  bool is_super_call =
418
      // The linked function is a SuperFunction
419
      ( fc.function_link->user_function() &&
12,879✔
420
        fc.function_link->user_function()->type == UserFunctionType::Super ) ||
38,521✔
421

422
      ( !fc.function_link->function() &&  // no linked function
13,087✔
423
        fc.scoped_name &&                 // there is a name in the call (ie. not an expression)
324✔
424
        Clib::caseInsensitiveEqual( fc.scoped_name->string(), "super" ) );  // the name is "super"
13,021✔
425

426
  // No function linked through FunctionResolver
427
  if ( !fc.function_link->function() )
12,879✔
428
  {
429
    // Method name may be set to variable name, eg: `var foo; foo();` If so,
430
    // clear it out and insert it at the children start to set as callee.
431
    if ( fc.scoped_name )
324✔
432
    {
433
      // If the a function is a class, then it did not define a constructor (since
434
      // there was no function linked).
435

436
      // If a function call is eg. `Animal()` with no scope, check the string as-is.
437
      std::string class_name = fc.string();
142✔
438

439
      auto class_itr = workspace.all_class_locations.find( class_name );
142✔
440
      if ( class_itr == workspace.all_class_locations.end() )
142✔
441
      {
442
        class_name = fc.scoped_name->scope.string();
136✔
443

444
        class_itr = workspace.all_class_locations.find( class_name );
136✔
445
        if ( class_itr == workspace.all_class_locations.end() )
136✔
446
        {
447
          class_name.clear();
130✔
448
        }
449
      }
450

451
      if ( !class_name.empty() )
142✔
452
      {
453
        // There may be a variable named the same as the class, eg:
454
        //
455
        //   class Animal() var Animal; endclass
456
        //
457
        // If that is the case, there will be a global named
458
        // `class_name::class_name`. We will not error in this case.
459
        if ( Clib::caseInsensitiveEqual( fc.scoped_name->name, class_name ) &&
22✔
460
             !globals.find( ScopableName( class_name, class_name ).string() ) )
22✔
461
        {
462
          auto msg = fmt::format( "In function call: Class '{}' does not define a constructor.",
463
                                  class_name );
×
464

465
          auto func_itr = workspace.all_function_locations.find(
8✔
466
              ScopableName( class_name, class_name ).string() );
16✔
467

468
          if ( func_itr != workspace.all_function_locations.end() )
8✔
469
          {
470
            msg += fmt::format( "\n  See also (missing 'this' parameter?): {}", func_itr->second );
8✔
471
          }
472

473
          report.error( fc, msg );
8✔
474
        }
8✔
475
      }
476

477
      auto callee = std::make_unique<Identifier>( fc.source_location, *fc.scoped_name );
142✔
478
      fc.children.insert( fc.children.begin(), std::move( callee ) );
142✔
479
      fc.scoped_name.reset();
142✔
480
    }
142✔
481

482
    // For function calls where the callee is not an identifier, take the
483
    // arguments as-is. We don't support named args, as we don't know the
484
    // function to execute until runtime.
485
    auto any_named = std::find_if( fc.children.begin() + 1, fc.children.end(),
324✔
486
                                   []( const std::unique_ptr<Node>& node )
624✔
487
                                   {
488
                                     const auto& arg_name =
489
                                         static_cast<Argument*>( node.get() )->identifier;
624✔
490
                                     return arg_name != nullptr;
624✔
491
                                   } );
492

493
    if ( any_named != fc.children.end() )
324✔
494
    {
495
      report.error( fc, "In function call: Cannot use named arguments here." );
2✔
496

497
      return;
2✔
498
    }
499

500
    visit_children( fc );
322✔
501

502
    return;
322✔
503
  }
504

505
  // here we turn the arguments passed (which can be named or positional)
506
  // into the final_arguments vector, which is just one parameter per
507
  // argument, in the correct order.
508

509
  typedef std::map<std::string, std::unique_ptr<Expression>> ArgumentList;
510
  ArgumentList arguments_passed;
12,555✔
511

512
  typedef std::list<std::unique_ptr<Expression>> VariadicArguments;
513
  VariadicArguments variadic_arguments;
12,555✔
514

515
  bool any_named = false;
12,555✔
516
  auto uf = fc.function_link->user_function();
12,555✔
517

518
  std::vector<std::unique_ptr<Argument>> arguments = fc.take_arguments();
12,555✔
519
  auto parameters = fc.parameters();
12,555✔
520
  bool has_class_inst_parameter = false;
12,555✔
521

522
  bool in_super_func = false;
12,555✔
523
  bool in_constructor_func = false;
12,555✔
524
  bool in_generated_function = false;
12,555✔
525

526
  if ( !user_functions.empty() )
12,555✔
527
  {
528
    if ( user_functions.top()->type == UserFunctionType::Super )
5,862✔
529
    {
530
      in_super_func = true;
174✔
531
    }
532
    else if ( user_functions.top()->type == UserFunctionType::Constructor )
5,688✔
533
    {
534
      in_constructor_func = true;
248✔
535
      in_generated_function = dynamic_cast<GeneratedFunction*>( user_functions.top() );
248✔
536
    }
537
  }
538

539
  if ( uf )
12,555✔
540
  {
541
    // A super() call can only be used in a constructor function.
542
    if ( is_super_call && !in_constructor_func )
3,714✔
543
    {
544
      report.error( fc, "In call to '{}': super() can only be used in constructor functions.",
2✔
545
                    uf->name );
2✔
546
      return;
2✔
547
    }
548
    // Constructor functions are defined as `Constr( this )` and called
549
    // statically via `Constr()`. Provide a `this` parameter at this function
550
    // call site. Only do this when calling constructors outside of a
551
    // compiler-generated function (ie. super or generated constructor)
552
    else if ( uf->type == UserFunctionType::Constructor && !in_generated_function &&
3,712✔
553
              !in_super_func )
324✔
554
    {
555
      // A super call inherits the `this` argument
556
      if ( is_super_call )
150✔
557
      {
558
        // Super will use "this" argument
559
        arguments.insert( arguments.begin(),
×
560
                          std::make_unique<Argument>(
×
561
                              fc.source_location,
×
562
                              std::make_unique<Identifier>( fc.source_location, "this" ), false ) );
×
563

564
        report.debug( fc, "using ctor Identifier is_super_call={} in_super_func={} uf->name={}",
×
565
                      is_super_call, in_super_func, uf->name );
×
566
      }
567
      else
568
      {
569
        if ( dynamic_cast<GeneratedFunction*>( uf ) != nullptr )
150✔
570
        {
571
          if ( uf->body().children.empty() )
6✔
572
          {
573
            report.error( fc, "In call to '{}': No base class defines a constructor.", uf->name );
2✔
574
            return;
2✔
575
          }
576
        }
577

578
        // Should never happen
579
        if ( uf->class_link == nullptr || uf->class_link->class_declaration() == nullptr )
148✔
580
        {
581
          uf->internal_error(
×
582
              fmt::format( "no class declaration found for user function '{}'", uf->name ) );
×
583
        }
584

585
        // Constructor will create a new "this" instance
586
        arguments.insert( arguments.begin(),
148✔
587
                          std::make_unique<Argument>(
148✔
588
                              fc.source_location,
148✔
589
                              std::make_unique<ClassInstance>(
148✔
590
                                  fc.source_location, uf->class_link->class_declaration() ),
148✔
591
                              false ) );
148✔
592

593
        report.debug( fc, "using ClassInstance is_super_call={} in_super_func={} uf->name={}",
148✔
594
                      is_super_call, in_super_func, uf->name );
148✔
595
      }
596
      // Since a `this` argument is generated for constructor functions, disallow passing an
597
      // argument named `this`.
598
      has_class_inst_parameter = true;
148✔
599
    }
600
    else if ( uf->type == UserFunctionType::Super )
3,562✔
601
    {
602
      if ( uf->body().children.empty() )
114✔
603
      {
604
        report.error( fc, "In call to '{}': No base class defines a constructor.", uf->name );
2✔
605
        return;
2✔
606
      }
607
      // Super will use "this" argument
608
      arguments.insert( arguments.begin(),
112✔
609
                        std::make_unique<Argument>(
112✔
610
                            fc.source_location,
112✔
611
                            std::make_unique<Identifier>( fc.source_location, "this" ), false ) );
224✔
612

613
      report.debug( fc, "using super Identifier is_super_call={} in_super_func={} uf->name={}",
112✔
614
                    is_super_call, in_super_func, uf->name );
112✔
615
    }
616
  }
617

618
  auto is_callee_variadic = parameters.size() && parameters.back().get().rest;
12,549✔
619

620
  const auto method_name = fc.string();
12,549✔
621

622
  for ( auto& arg_unique_ptr : arguments )
29,454✔
623
  {
624
    auto& arg = *arg_unique_ptr;
16,911✔
625
    std::string arg_name = arg.identifier ? arg.identifier->string() : "";
16,911✔
626

627
    if ( arg.spread )
16,911✔
628
    {
629
      if ( !uf )  // a module function
124✔
630
      {
631
        report.error( arg,
2✔
632
                      "In call to '{}': Spread operator cannot be used in module function call.",
633
                      method_name );
634
        return;
2✔
635
      }
636
      else if ( !uf->is_variadic() )
122✔
637
      {
638
        report.error( arg,
2✔
639
                      "In call to '{}': Spread operator can only be used in variadic functions.",
640
                      method_name );
641
        return;
2✔
642
      }
643
      else if ( arguments_passed.size() < parameters.size() - 1 )
120✔
644
      {
645
        report.error( arg,
2✔
646
                      "In call to '{}': Spread operator can only be used for arguments on or after "
647
                      "the formal rest parameter.",
648
                      method_name );
649
        return;
2✔
650
      }
651
    }
652

653
    if ( arg_name.empty() )
16,905✔
654
    {
655
      // Allow spread elements to come after named arguments, eg:
656
      //
657
      //  `foo( optA := 1, optB := 2, ... c )`
658
      //
659
      if ( any_named && !arg.spread )
16,261✔
660
      {
661
        report.error( arg, "In call to '{}': Unnamed args cannot follow named args.", method_name );
×
662
        return;
×
663
      }
664

665
      // Too many arguments passed?
666
      if ( arguments_passed.size() >= parameters.size() )
16,261✔
667
      {
668
        // Allowed if variadic
669
        if ( is_callee_variadic )
198✔
670
        {
671
          variadic_arguments.push_back( arg.take_expression() );
194✔
672

673
          // Do not add to `arguments_passed`, so continue.
674
          continue;
194✔
675
        }
676
        else
677
        {
678
          auto expected_args =
679
              static_cast<int>( parameters.size() ) - ( has_class_inst_parameter ? 1 : 0 );
4✔
680

681
          report.error( arg, "In call to '{}': Too many arguments passed.  Expected {}, got {}.",
8✔
682
                        method_name, expected_args, arguments.size() );
4✔
683
          continue;
4✔
684
        }
4✔
685
      }
686
      else
687
      {
688
        arg_name = parameters.at( arguments_passed.size() ).get().name.string();
16,063✔
689
      }
690
    }
691
    else
692
    {
693
      any_named = true;
644✔
694

695
      if ( has_class_inst_parameter && !in_super_func && !is_super_call &&
644✔
696
           Clib::caseInsensitiveEqual( arg_name, "this" ) )
644✔
697
      {
698
        report.error( arg, "In call to '{}': Cannot pass 'this' to constructor function.",
×
699
                      method_name );
700
        return;
×
701
      }
702
    }
703

704
    if ( arguments_passed.find( arg_name ) != arguments_passed.end() )
16,707✔
705
    {
706
      report.error( arg, "In call to '{}': Parameter '{}' passed more than once.", method_name,
×
707
                    arg_name );
708
      return;
×
709
    }
710

711
    // Inside a call to super(), if the arg is un-scoped, find the base class it belongs to. Error
712
    // if ambiguous.
713
    if ( is_super_call )
16,707✔
714
    {
715
      if ( arg.identifier && arg.identifier->global() && arg.identifier->string() != "this" )
238✔
716
      {
717
        std::string base_class;
12✔
718
        std::string first_location;
12✔
719
        std::string err_msg;
12✔
720

721
        auto add_location = [this]( std::string& where, const std::string& class_name )
14✔
722
        {
723
          fmt::format_to( std::back_inserter( where ), "  See: {}", class_name );
14✔
724

725
          auto funct_itr = workspace.all_function_locations.find(
14✔
726
              ScopableName( class_name, class_name ).string() );
28✔
727
          if ( funct_itr != workspace.all_function_locations.end() )
14✔
728
          {
729
            fmt::format_to( std::back_inserter( where ), " {}\n", funct_itr->second );
42✔
730
          }
731
          else
732
          {
733
            where += "\n";
×
734
          }
735
        };
14✔
736

737
        for ( auto& param_ref : parameters )
58✔
738
        {
739
          auto& param = param_ref.get();
46✔
740
          const auto& param_name = param.name.name;
46✔
741
          if ( Clib::caseInsensitiveEqual( param_name, arg_name ) )
46✔
742
          {
743
            if ( !base_class.empty() )
14✔
744
            {
745
              if ( err_msg.empty() )
2✔
746
              {
747
                fmt::format_to( std::back_inserter( err_msg ),
4✔
748
                                "In call to '{}': Ambiguous parameter '{}'.\n{}", method_name,
749
                                param_name, first_location );
750
              }
751

752
              add_location( err_msg, param.name.scope.string() );
2✔
753
            }
754
            else
755
            {
756
              base_class = param.name.scope.string();
12✔
757

758
              add_location( first_location, base_class );
12✔
759
            }
760
          }
761
        }
762
        if ( !err_msg.empty() )
12✔
763
        {
764
          report.error( fc, err_msg );
2✔
765
        }
766
        else
767
        {
768
          arg_name = ScopableName( base_class, arg_name ).string();
10✔
769
        }
770
      }
12✔
771
    }
772

773

774
    arguments_passed[arg_name] = arg.take_expression();
16,707✔
775
  }
16,911✔
776

777
  std::vector<std::unique_ptr<Node>> final_arguments;
12,543✔
778

779
  for ( auto& param_ref : parameters )
36,319✔
780
  {
781
    FunctionParameterDeclaration& param = param_ref.get();
23,790✔
782
    auto itr = arguments_passed.find( param.name.string() );
23,790✔
783
    if ( itr == arguments_passed.end() )
23,790✔
784
    {
785
      if ( auto default_value = param.default_value() )
7,093✔
786
      {
787
        SimpleValueCloner cloner( report, default_value->source_location );
7,061✔
788
        auto final_argument = cloner.clone( *default_value );
7,061✔
789

790
        if ( final_argument )
7,061✔
791
        {
792
          final_arguments.push_back( std::move( final_argument ) );
7,059✔
793
        }
794
        else
795
        {
796
          report.error(
2✔
797
              param, "In call to '{}': Unable to create argument from default for parameter '{}'.",
798
              method_name, param.name );
2✔
799
          return;
2✔
800
        }
801
      }
7,063✔
802
      else if ( !param.rest )
32✔
803
      {
804
        report.error( fc,
12✔
805
                      "In call to '{}': Parameter '{}' was not passed, and there is no default.",
806
                      method_name, param.name );
12✔
807
        return;
12✔
808
      }
809
    }
810
    else
811
    {
812
      final_arguments.push_back( std::move( ( *itr ).second ) );
16,697✔
813
      arguments_passed.erase( itr );
16,697✔
814
    }
815
  }
816

817
  if ( is_callee_variadic )
12,529✔
818
  {
819
    // Push the leftover arguments into the call.
820
    for ( auto& arg : variadic_arguments )
360✔
821
    {
822
      final_arguments.push_back( std::move( arg ) );
194✔
823
    }
824
  }
825
  else
826
  {
827
    for ( auto& unused_argument : arguments_passed )
12,365✔
828
    {
829
      report.error( *unused_argument.second,
2✔
830
                    "In call to '{}': Parameter '{}' passed by name, but the function has no "
831
                    "such parameter.",
832
                    method_name, unused_argument.first );
2✔
833
    }
834
    if ( !arguments_passed.empty() || arguments.size() > parameters.size() )
12,363✔
835
      return;
6✔
836
  }
837

838
  fc.children = std::move( final_arguments );
12,523✔
839

840
  // do this afterwards, so that named parameters will not be looked up as identifiers.
841
  visit_children( fc );
12,523✔
842
}
12,697✔
843

844
void SemanticAnalyzer::visit_function_parameter_list( FunctionParameterList& node )
2,112✔
845
{
846
  // A rest parameter can only be the last parameter in the list. Since we
847
  // iterate in reverse, start at `true` and set to `false` after first
848
  // iteration.
849
  bool can_have_rest_parameter = true;
2,112✔
850

851
  for ( auto& child : boost::adaptors::reverse( node.children ) )
4,608✔
852
  {
853
    child->accept( *this );
2,496✔
854

855
    bool has_rest_parameter = static_cast<FunctionParameterDeclaration*>( child.get() )->rest;
2,496✔
856

857
    if ( has_rest_parameter && !can_have_rest_parameter )
2,496✔
858
    {
859
      report.error( *child, "Rest parameter must be the last parameter in the list." );
2✔
860
    }
861

862
    can_have_rest_parameter = false;
2,496✔
863
  }
864
}
2,112✔
865

866
void SemanticAnalyzer::visit_function_parameter_declaration( FunctionParameterDeclaration& node )
2,496✔
867
{
868
  auto node_name = node.name.string();
2,496✔
869
  if ( auto default_value = node.default_value() )
2,496✔
870
  {
871
    ConstantValidator validator;
214✔
872
    // By accident, 0-parameter system function calls are allowed as constant values.
873
    // They are not allowed as default parameters, though.
874
    if ( !validator.validate( *default_value ) || dynamic_cast<FunctionCall*>( default_value ) )
214✔
875
    {
876
      report.error( node,
6✔
877
                    "Parameter '{}' has a disallowed default.  Only simple operands are allowed as "
878
                    "default arguments.",
879
                    node_name );
880
      // but continue, to avoid unknown identifier errors
881
    }
882
  }
214✔
883
  if ( auto existing = locals.find( node_name ) )
2,496✔
884
  {
885
    report.error( node, "Parameter '{}' already defined.", node_name );
×
886
    return;
×
887
  }
2,496✔
888

889
  if ( node.rest && node.default_value() )
2,496✔
890
  {
891
    report.error( node, "Rest parameter '{}' cannot have a default value.", node_name );
2✔
892
    return;
2✔
893
  }
894

895
  WarnOn warn_on = node.unused ? WarnOn::IfUsed : WarnOn::IfNotUsed;
2,494✔
896

897
  if ( report_function_name_conflict( node.source_location, node_name, "function parameter" ) )
2,494✔
898
  {
899
    warn_on = WarnOn::Never;
2✔
900
  }
901

902
  local_scopes.current_local_scope()->create( node_name, warn_on, node.source_location );
2,494✔
903
}
2,496✔
904

905
void SemanticAnalyzer::visit_function_expression( FunctionExpression& node )
222✔
906
{
907
  if ( auto user_function = node.function_link->user_function() )
222✔
908
  {
909
    // Create a new capture scope for this function. It must be in a new C++
910
    // scope for to add the captures to
911
    // `user_function->capture_variable_scope_info` via the user function
912
    // visitor.
913
    {
914
      FunctionVariableScope new_capture_scope( captures );
222✔
915
      LocalVariableScope capture_scope( capture_scopes,
222✔
916
                                        user_function->capture_variable_scope_info );
222✔
917
      FunctionVariableScope new_function_scope( locals );
222✔
918
      visit_user_function( *user_function );
222✔
919
    }
222✔
920

921
    // Since the capture_scope was popped (above), any _existing_ capture scope refers to
922
    // the parent function expression in the tree. Adjust that function to inherit this function's
923
    // captures.
924
    if ( auto cap_scope = capture_scopes.current_local_scope() )
222✔
925
    {
926
      for ( auto& variable : user_function->capture_variable_scope_info.variables )
276✔
927
      {
928
        // If the capture is not local, we must capture it
929
        if ( !locals.find( variable->name ) )
200✔
930
        {
931
          // If already captured, set the variables capture to the existing.
932
          if ( auto captured = captures.find( variable->name ) )
118✔
933
          {
934
            variable->capturing = captured;
64✔
935
          }
936
          // Otherwise, create new.
937
          else if ( !captures.find( variable->name ) )
54✔
938
          {
939
            // Create a new capture variable in the parent function, setting
940
            // this function expression's captured variable to this newly
941
            // created one.
942
            variable->capturing = cap_scope->capture( variable->capturing );
54✔
943
          }
118✔
944
        }
945
      }
946
    }
947
  }
948
}
222✔
949

950
void SemanticAnalyzer::visit_function_reference( FunctionReference& node )
482✔
951
{
952
  if ( auto function = node.function_link->user_function() )
482✔
953
  {
954
    if ( function->type == UserFunctionType::Super )
478✔
955
    {
956
      report.error( node, "Cannot reference super() function." );
2✔
957
    }
958
  }
959
  else
960
  {
961
    report.error( node, "User function '{}' not found", node.name );
4✔
962
  }
963
}
482✔
964

965
void SemanticAnalyzer::visit_identifier( Identifier& node )
21,566✔
966
{
967
  // Resolution order:
968
  //
969
  // if scoped: locals -> globals
970
  // otherwise: local function -> local captures -> ancestor (above) functions -> globals
971
  //
972
  const auto& name = node.scoped_name.string();
21,566✔
973

974
  // If there is a scope, whether it is (":foo") empty or not ("Animal:foo"),
975
  // we need to check both globals and locals.
976
  if ( !node.scoped_name.scope.empty() )
21,566✔
977
  {
978
    if ( !node.scoped_name.scope.global() )
200✔
979
    {
980
      if ( auto local = locals.find( name ) )
186✔
981
      {
982
        local->mark_used();
134✔
983
        node.variable = local;
134✔
984
      }
186✔
985
    }
986
    // Did not find it in locals, check globals
987
    if ( !node.variable )
200✔
988
    {
989
      if ( auto scoped_global = globals.find( name ) )
66✔
990
      {
991
        node.variable = scoped_global;
58✔
992
      }
66✔
993
    }
994
  }
995
  else
996
  {
997
    if ( auto local = locals.find( name ) )
21,366✔
998
    {
999
      local->mark_used();
14,996✔
1000
      node.variable = local;
14,996✔
1001
    }
1002
    else if ( auto captured = captures.find( name ) )
6,370✔
1003
    {
1004
      // Should already be marked used as it's not newly created (done below).
1005
      // There is no `captures.find_in_ancestors()` check because if an upper
1006
      // capture was found, we still need to capture it for our own function (done
1007
      // below).
1008
      node.variable = captured;
56✔
1009
    }
1010
    else if ( auto ancestor = locals.find_in_ancestors( name ) )
6,314✔
1011
    {
1012
      // Capture the variable. In a deeply nested capture, this will reference the
1013
      // local in the ancestor function. The function expression visitor will swap
1014
      // the 'capturing' to a local-safe variable.
1015
      node.variable = capture_scopes.current_local_scope()->capture( ancestor );
190✔
1016
      node.variable->mark_used();
190✔
1017
    }
1018
    else if ( auto global = globals.find( name ) )
6,124✔
1019
    {
1020
      node.variable = global;
6,084✔
1021
    }
1022
    else if ( !current_scope_name().global() )
40✔
1023
    {
1024
      const auto scoped_name = ScopableName( current_scope_name(), node.name() ).string();
8✔
1025

1026
      // We do not support nested classes, so if there is a `current_scope`, it would only _ever_
1027
      // exist in globals.
1028
      if ( auto scoped_global = globals.find( scoped_name ) )
8✔
1029
      {
1030
        node.variable = scoped_global;
8✔
1031
      }
8✔
1032
    }
40,182✔
1033
  }
1034

1035
  if ( !node.variable )
21,566✔
1036
  {
1037
    report.error( node, "Unknown identifier '{}'.", name );
40✔
1038
    return;
40✔
1039
  }
1040
}
21,566✔
1041

1042
void SemanticAnalyzer::visit_variable_binding( VariableBinding& node )
570✔
1043
{
1044
  if ( auto variable = create_variable( node.source_location, node.scoped_name.scope.string(),
1,140✔
1045
                                        node.scoped_name.name ) )
1,140✔
1046
  {
1047
    node.variable = std::move( variable );
570✔
1048
    visit_children( node );
570✔
1049
  }
570✔
1050
}
570✔
1051

1052
void SemanticAnalyzer::visit_jump_statement( JumpStatement& node )
530✔
1053
{
1054
  auto& scopes = node.jump_type == JumpStatement::Break ? break_scopes : continue_scopes;
530✔
1055

1056
  if ( auto scope = scopes.find( node.label ) )
530✔
1057
  {
1058
    node.flow_control_label = scope->flow_control_label;
530✔
1059
    node.local_variables_to_remove = locals.count() - scope->local_variables_size;
530✔
1060
  }
1061
  else
1062
  {
1063
    auto type_str = node.jump_type == JumpStatement::Break ? "break" : "continue";
×
1064

1065
    if ( !node.label.empty() && break_scopes.any() )
×
1066
      report.error( node, "Label '{}' not found for {}", node.label, type_str );
×
1067
    else
1068
      report.error( node, "Cannot {} here.", type_str );
×
1069
  }
1070
}
530✔
1071

1072

1073
void SemanticAnalyzer::visit_loop_statement( LoopStatement& loop )
408✔
1074
{
1075
  FlowControlScope continue_scope( continue_scopes, loop.source_location, loop.get_label(),
408✔
1076
                                   loop.continue_label );
816✔
1077
  FlowControlScope break_scope( break_scopes, loop.source_location, loop.get_label(),
408✔
1078
                                loop.break_label );
816✔
1079

1080
  visit_children( loop );
408✔
1081
}
408✔
1082

1083
void SemanticAnalyzer::visit_program( Program& program )
353✔
1084
{
1085
  LocalVariableScope scope( local_scopes, program.local_variable_scope_info );
353✔
1086

1087
  visit_children( program );
353✔
1088
}
353✔
1089

1090
void SemanticAnalyzer::visit_program_parameter_declaration( ProgramParameterDeclaration& node )
158✔
1091
{
1092
  if ( auto existing = locals.find( node.name ) )
158✔
1093
  {
1094
    report.error( node, "Parameter '{}' already defined.", node.name );
×
1095
    return;
×
1096
  }
158✔
1097
  WarnOn warn_on = node.unused ? WarnOn::IfUsed : WarnOn::IfNotUsed;
158✔
1098

1099
  if ( report_function_name_conflict( node.source_location, node.name, "program parameter" ) )
158✔
1100
  {
1101
    warn_on = WarnOn::Never;
2✔
1102
  }
1103

1104
  local_scopes.current_local_scope()->create( node.name, warn_on, node.source_location );
158✔
1105
}
1106

1107
void SemanticAnalyzer::visit_repeat_until_loop( RepeatUntilLoop& node )
26✔
1108
{
1109
  visit_loop_statement( node );
26✔
1110
}
26✔
1111

1112
void SemanticAnalyzer::visit_return_statement( ReturnStatement& node )
3,436✔
1113
{
1114
  if ( !user_functions.empty() )
3,436✔
1115
  {
1116
    auto uf = user_functions.top();
3,164✔
1117

1118
    if ( uf->type == UserFunctionType::Constructor && !node.children.empty() )
3,164✔
1119
    {
1120
      report.error( node, "Cannot return a value from a constructor function." );
2✔
1121
    }
1122
  }
1123

1124
  visit_children( node );
3,436✔
1125
}
3,436✔
1126

1127
void SemanticAnalyzer::visit_sequence_binding( SequenceBinding& node )
70✔
1128
{
1129
  u8 index = 0;
70✔
1130
  VariableBinding* previous_rest_binding = nullptr;
70✔
1131

1132
  if ( node.binding_count() > 127 )
70✔
1133
  {
NEW
1134
    report.error( node, "Too many binding elements. Maximum is 127." );
×
1135
  }
1136

1137
  for ( const auto& child : node.children )
284✔
1138
  {
1139
    if ( auto index_binding = dynamic_cast<VariableBinding*>( child.get() ) )
214✔
1140
    {
1141
      if ( index_binding->rest )
204✔
1142
      {
1143
        if ( previous_rest_binding != nullptr )
50✔
1144
        {
1145
          report.error( *index_binding,
2✔
1146
                        "Only one rest binding is allowed.\n"
1147
                        "  See also: {}",
1148
                        previous_rest_binding->source_location );
2✔
1149
        }
1150

1151
        previous_rest_binding = index_binding;
50✔
1152
        node.rest_index = index;
50✔
1153
      }
1154
    }
1155

1156
    ++index;
214✔
1157
  }
1158

1159
  visit_children( node );
70✔
1160
}
70✔
1161

1162
void SemanticAnalyzer::visit_user_function( UserFunction& node )
2,112✔
1163
{
1164
  // Track current scope for use in visit_identifier
1165
  current_scope_names.push( ScopeName( node.scope ) );
2,112✔
1166
  user_functions.emplace( &node );
2,112✔
1167
  if ( node.exported )
2,112✔
1168
  {
1169
    if ( !node.scope.empty() )
484✔
1170
    {
1171
      report.error( node, "Exported function '{}' cannot be scoped.", node.scoped_name() );
2✔
1172
    }
1173
    else
1174
    {
1175
      unsigned max_name_length = sizeof( Pol::Bscript::BSCRIPT_EXPORTED_FUNCTION::funcname ) - 1;
482✔
1176
      if ( node.name.length() > max_name_length )
482✔
1177
      {
1178
        report.error( node,
4✔
1179
                      "Exported function name '{}' is too long at {} characters.  Max length: {}",
1180
                      node.name, node.name.length(), max_name_length );
2✔
1181
      }
1182
    }
1183
  }
1184

1185
  LocalVariableScope scope( local_scopes, node.local_variable_scope_info );
2,112✔
1186
  visit_children( node );
2,112✔
1187
  user_functions.pop();
2,112✔
1188
  current_scope_names.pop();
2,112✔
1189
}
2,112✔
1190

1191
void SemanticAnalyzer::visit_var_statement( VarStatement& node )
4,096✔
1192
{
1193
  if ( auto variable = create_variable( node.source_location, node.scope, node.name ) )
4,096✔
1194
  {
1195
    node.variable = std::move( variable );
4,092✔
1196
    visit_children( node );
4,092✔
1197
  }
4,096✔
1198
}
4,096✔
1199

1200
void SemanticAnalyzer::visit_variable_assignment_statement( VariableAssignmentStatement& node )
1,390✔
1201
{
1202
  visit_children( node );
1,390✔
1203

1204
  if ( auto bop = dynamic_cast<BinaryOperator*>( &node.rhs() ) )
1,390✔
1205
  {
1206
    if ( bop->token_id == TOK_ASSIGN )
118✔
1207
    {
1208
      if ( auto second_ident = dynamic_cast<Identifier*>( &bop->lhs() ) )
2✔
1209
      {
1210
        if ( node.identifier().variable == second_ident->variable )
×
1211
        {
1212
          // we have something like
1213
          //      a := a := expr;
1214
          report.warning( node, "Double-assignment to the same variable '{}'.",
×
1215
                          node.identifier().name() );
×
1216
        }
1217
      }
1218
    }
1219
  }
1220
}
1,390✔
1221

1222
void SemanticAnalyzer::visit_while_loop( WhileLoop& node )
310✔
1223
{
1224
  visit_loop_statement( node );
310✔
1225
}
310✔
1226

1227
std::shared_ptr<Variable> SemanticAnalyzer::create_variable( const SourceLocation& source_location,
4,666✔
1228
                                                             const std::string& scope,
1229
                                                             const std::string& name )
1230
{
1231
  auto maybe_scoped_name = ScopableName( scope, name ).string();
4,666✔
1232

1233
  // Since this is not scoped check, we cannot have `Animal::FOO` and a constant `FOO`.
1234
  if ( auto constant = workspace.constants.find( name ) )
4,666✔
1235
  {
1236
    report.error( source_location,
4✔
1237
                  "Cannot define a variable with the same name as constant '{}'.\n"
1238
                  "  See also: {}",
1239
                  name, constant->source_location );
4✔
1240
    return {};
4✔
1241
  }
1242

1243
  report_function_name_conflict( source_location, maybe_scoped_name, "variable" );
4,662✔
1244

1245
  if ( auto local_scope = local_scopes.current_local_scope() )
4,662✔
1246
  {
1247
    return local_scope->create( maybe_scoped_name, WarnOn::Never, source_location );
2,826✔
1248
  }
1249
  else
1250
  {
1251
    if ( auto existing = globals.find( maybe_scoped_name ) )
1,836✔
1252
    {
NEW
1253
      report.error( source_location,
×
1254
                    "Global variable '{}' already defined.\n"
1255
                    "  See also: {}",
NEW
1256
                    maybe_scoped_name, existing->source_location );
×
NEW
1257
      return {};
×
1258
    }
1,836✔
1259

1260
    return globals.create( maybe_scoped_name, 0, WarnOn::Never, source_location );
1,836✔
1261
  }
1262
}
4,666✔
1263

1264
bool SemanticAnalyzer::report_function_name_conflict( const SourceLocation& referencing_loc,
7,720✔
1265
                                                      const std::string& function_name,
1266
                                                      const std::string& element_description )
1267
{
1268
  return report_function_name_conflict(
15,440✔
1269
      workspace, report, referencing_loc,
7,720✔
1270
      ScopableName( current_scope_name(), function_name ).string(), element_description );
15,440✔
1271
}
1272

1273
bool SemanticAnalyzer::report_function_name_conflict( const CompilerWorkspace& workspace,
121,715✔
1274
                                                      Report& report,
1275
                                                      const SourceLocation& referencing_loc,
1276
                                                      const std::string& function_name,
1277
                                                      const std::string& element_description )
1278
{
1279
  auto func_itr = workspace.all_function_locations.find( function_name );
121,715✔
1280
  if ( func_itr != workspace.all_function_locations.end() )
121,715✔
1281
  {
1282
    const SourceLocation& function_loc = ( *func_itr ).second;
16✔
1283
    report.error( referencing_loc,
16✔
1284
                  "Cannot define a {} with the same name as function '{}'.\n"
1285
                  "  Defined here: {}",
1286
                  element_description, function_name, function_loc );
1287
    return true;
16✔
1288
  }
1289
  return false;
121,699✔
1290
}
1291

1292
}  // namespace Pol::Bscript::Compiler
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc