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

polserver / polserver / 17472095514

04 Sep 2025 05:37PM UTC coverage: 59.828% (+0.02%) from 59.805%
17472095514

push

github

web-flow
Fix generated function (constructor, super) registration (#809)

* move compiler-generated ctor registration to within class declaration registration; update test error messages

* add more ctor inheritance tests

* skip "Unknown identifier" error for calling undefined ctor

* add test for actual, previously crashing source

* add test for actual, previously crashing source

* move compiler-generator super registration to within class declaration registration

* add super local var test

* make register_available_generated_function private

* remove ClassDeclaration::has_super_ctor

This property was based on if FunctionResolver created the function, so
the logic can be moved to FunctionResolver.

* add core-changes

* use constant for "super" string

68 of 71 new or added lines in 4 files covered. (95.77%)

3 existing lines in 2 files now uncovered.

43721 of 73078 relevant lines covered (59.83%)

433591.61 hits per line

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

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

3
#include <list>
4
#include <ranges>
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/ConstantPredicateLoop.h"
28
#include "bscript/compiler/ast/CstyleForLoop.h"
29
#include "bscript/compiler/ast/DoWhileLoop.h"
30
#include "bscript/compiler/ast/ForeachLoop.h"
31
#include "bscript/compiler/ast/FunctionBody.h"
32
#include "bscript/compiler/ast/FunctionCall.h"
33
#include "bscript/compiler/ast/FunctionExpression.h"
34
#include "bscript/compiler/ast/FunctionParameterDeclaration.h"
35
#include "bscript/compiler/ast/FunctionParameterList.h"
36
#include "bscript/compiler/ast/FunctionReference.h"
37
#include "bscript/compiler/ast/GeneratedFunction.h"
38
#include "bscript/compiler/ast/Identifier.h"
39
#include "bscript/compiler/ast/IndexBinding.h"
40
#include "bscript/compiler/ast/IntegerValue.h"
41
#include "bscript/compiler/ast/JumpStatement.h"
42
#include "bscript/compiler/ast/MemberAccess.h"
43
#include "bscript/compiler/ast/ModuleFunctionDeclaration.h"
44
#include "bscript/compiler/ast/Program.h"
45
#include "bscript/compiler/ast/ProgramParameterDeclaration.h"
46
#include "bscript/compiler/ast/RepeatUntilLoop.h"
47
#include "bscript/compiler/ast/ReturnStatement.h"
48
#include "bscript/compiler/ast/SequenceBinding.h"
49
#include "bscript/compiler/ast/StringValue.h"
50
#include "bscript/compiler/ast/TopLevelStatements.h"
51
#include "bscript/compiler/ast/UserFunction.h"
52
#include "bscript/compiler/ast/VarStatement.h"
53
#include "bscript/compiler/ast/VariableAssignmentStatement.h"
54
#include "bscript/compiler/ast/VariableBinding.h"
55
#include "bscript/compiler/ast/WhileLoop.h"
56
#include "bscript/compiler/astbuilder/SimpleValueCloner.h"
57
#include "bscript/compiler/model/ClassLink.h"
58
#include "bscript/compiler/model/CompilerWorkspace.h"
59
#include "bscript/compiler/model/FunctionLink.h"
60
#include "bscript/compiler/model/ScopeName.h"
61
#include "bscript/compiler/model/Variable.h"
62
#include "bscript/compiler/optimizer/ConstantValidator.h"
63
#include "clib/strutil.h"
64
#include "filefmt.h"
65

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

82
SemanticAnalyzer::~SemanticAnalyzer() = default;
2,263✔
83

84
void SemanticAnalyzer::register_const_declarations( CompilerWorkspace& workspace, Report& report )
2,275✔
85
{
86
  for ( auto& constant : workspace.const_declarations )
159,181✔
87
  {
88
    report_function_name_conflict( workspace, report, constant->source_location,
470,718✔
89
                                   constant->name.string(), "constant" );
313,812✔
90
    workspace.constants.create( *constant );
156,906✔
91
  }
92
}
2,275✔
93

94
void SemanticAnalyzer::analyze()
2,263✔
95
{
96
  workspace.top_level_statements->accept( *this );
2,263✔
97
  if ( auto& program = workspace.program )
2,263✔
98
  {
99
    program->accept( *this );
450✔
100
  }
101

102
  for ( auto& user_function : workspace.user_functions )
5,433✔
103
  {
104
    // Function expressions are analyzed within visit_function_expression.
105
    if ( !user_function->expression )
3,170✔
106
    {
107
      user_function->accept( *this );
2,630✔
108
    }
109
  }
110

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

118
  workspace.global_variable_names = globals.get_names();
2,263✔
119
}
2,263✔
120

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

133
  node.first().accept( *this );
68✔
134
  node.last().accept( *this );
68✔
135

136
  LocalVariableScope scope( local_scopes, node.local_variable_scope_info );
68✔
137
  scope.create( node.identifier, WarnOn::Never, node.source_location );
68✔
138
  scope.create( "_" + node.identifier + "_end", WarnOn::Never, node.source_location );
68✔
139

140
  FlowControlScope break_scope( break_scopes, node.source_location, node.get_label(),
68✔
141
                                node.break_label );
136✔
142
  FlowControlScope continue_scope( continue_scopes, node.source_location, node.get_label(),
68✔
143
                                   node.continue_label );
136✔
144

145
  node.block().accept( *this );
68✔
146
}
68✔
147

148
void SemanticAnalyzer::visit_block( Block& block )
5,803✔
149
{
150
  LocalVariableScope scope( local_scopes, block.local_variable_scope_info );
5,803✔
151

152
  visit_children( block );
5,803✔
153
}
5,803✔
154

155
void SemanticAnalyzer::visit_index_binding( IndexBinding& node )
89✔
156
{
157
  u8 index = 0;
89✔
158

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

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

173
        node.rest_index = index;
29✔
174
      }
175
    }
176

177
    ++index;
470✔
178
  }
89✔
179

180
  visit_children( node );
89✔
181
}
89✔
182

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

190
  report.debug( node, "Class '{}' declared with {} parameters", class_name,
×
191
                node.parameters().size() );
507✔
192

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

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

216
    bool previously_referenced =
217
        named_baseclasses.find( baseclass_name ) != named_baseclasses.end();
335✔
218

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

233
  for ( auto to_visit_itr = to_visit.begin(); to_visit_itr != to_visit.end();
952✔
234
        to_visit_itr = to_visit.erase( to_visit_itr ) )
445✔
235
  {
236
    auto cd = *to_visit_itr;
445✔
237
    if ( visited.find( cd ) != visited.end() )
445✔
238
    {
239
      continue;
53✔
240
    }
241

242
    visited.insert( cd );
392✔
243

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

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

264
class CaseDispatchDuplicateSelectorAnalyzer : public NodeVisitor
265
{
266
public:
267
  explicit CaseDispatchDuplicateSelectorAnalyzer( Report& report ) : report( report ) {}
110✔
268

269
  void visit_block( Block& ) override
348✔
270
  {
271
    // just don't recurse into children
272
  }
348✔
273

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

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

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

321
private:
322
  Report& report;
323

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

329

330
void SemanticAnalyzer::visit_case_statement( CaseStatement& case_ast )
110✔
331
{
332
  CaseDispatchDuplicateSelectorAnalyzer duplicate_detector( report );
110✔
333
  case_ast.dispatch_groups().accept( duplicate_detector );
110✔
334

335
  FlowControlScope break_scope( break_scopes, case_ast.source_location, case_ast.get_label(),
110✔
336
                                case_ast.break_label );
220✔
337

338
  visit_children( case_ast );
110✔
339
}
110✔
340

341
void SemanticAnalyzer::visit_case_dispatch_group( CaseDispatchGroup& dispatch_group )
348✔
342
{
343
  FlowControlScope break_scope( break_scopes, dispatch_group.source_location, "",
348✔
344
                                dispatch_group.break_label );
348✔
345

346
  visit_children( dispatch_group );
348✔
347
}
348✔
348

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

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

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

367
private:
368
  Report& report;
369
};
370

371
void SemanticAnalyzer::visit_case_dispatch_selectors( CaseDispatchSelectors& selectors )
348✔
372
{
373
  visit_children( selectors );
348✔
374

375
  CaseDispatchSelectorAnalyzer selector_analyzer( report );
348✔
376
  selectors.accept( selector_analyzer );
348✔
377
}
348✔
378

379
void SemanticAnalyzer::visit_cstyle_for_loop( CstyleForLoop& loop )
65✔
380
{
381
  visit_loop_statement( loop );
65✔
382
}
65✔
383

384
void SemanticAnalyzer::visit_do_while_loop( DoWhileLoop& do_while )
11✔
385
{
386
  visit_loop_statement( do_while );
11✔
387
}
11✔
388

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

397
  node.expression().accept( *this );
467✔
398

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

404
  FlowControlScope break_scope( break_scopes, node.source_location, node.get_label(),
467✔
405
                                node.break_label );
934✔
406
  FlowControlScope continue_scope( continue_scopes, node.source_location, node.get_label(),
467✔
407
                                   node.continue_label );
934✔
408

409
  node.block().accept( *this );
467✔
410
}
467✔
411

412
ScopeName& SemanticAnalyzer::current_scope_name()
11,131✔
413
{
414
  return current_scope_names.top();
11,131✔
415
}
416

417
void SemanticAnalyzer::visit_function_call( FunctionCall& fc )
17,868✔
418
{
419
  bool is_super_call =
420
      // The linked function is a SuperFunction
421
      ( fc.function_link->user_function() &&
17,868✔
422
        fc.function_link->user_function()->type == UserFunctionType::Super ) ||
53,448✔
423

424
      ( !fc.function_link->function() &&  // no linked function
18,188✔
425
        fc.scoped_name &&                 // there is a name in the call (ie. not an expression)
476✔
426
        Clib::caseInsensitiveEqual( fc.scoped_name->string(),
408✔
427
                                    Compiler::SUPER ) );  // the name is "super"
17,868✔
428

429
  // No function linked through FunctionResolver
430
  if ( !fc.function_link->function() )
17,868✔
431
  {
432
    if ( is_super_call && !globals.find( Compiler::SUPER ) && !locals.find( Compiler::SUPER ) )
496✔
433
    {
434
      report.error( fc, "In call to 'super': No base class defines a constructor." );
2✔
435
      return;  // skip "Unknown identifier" error
2✔
436
    }
437

438
    // Method name may be set to variable name, eg: `var foo; foo();` If so,
439
    // clear it out and insert it at the children start to set as callee.
440
    if ( fc.scoped_name )
474✔
441
    {
442
      // If the a function is a class, then it did not define a constructor (since
443
      // there was no function linked).
444

445
      // If a function call is eg. `Animal()` with no scope, check the string as-is.
446
      std::string class_name = fc.string();
202✔
447

448
      auto class_itr = workspace.all_class_locations.find( class_name );
202✔
449
      if ( class_itr == workspace.all_class_locations.end() )
202✔
450
      {
451
        class_name = fc.scoped_name->scope.string();
194✔
452

453
        class_itr = workspace.all_class_locations.find( class_name );
194✔
454
        if ( class_itr == workspace.all_class_locations.end() )
194✔
455
        {
456
          class_name.clear();
186✔
457
        }
458
      }
459

460
      if ( !class_name.empty() )
202✔
461
      {
462
        // There may be a variable named the same as the class, eg:
463
        //
464
        //   class Animal() var Animal; endclass
465
        //
466
        // If that is the case, there will be a global named
467
        // `class_name::class_name`. We will not error in this case.
468
        if ( Clib::caseInsensitiveEqual( fc.scoped_name->name, class_name ) &&
42✔
469
             !globals.find( ScopableName( class_name, class_name ).string() ) )
42✔
470
        {
471
          bool has_base_classes = false;
10✔
472

473
          if ( auto class_decl_itr = workspace.class_declaration_indexes.find( class_name );
10✔
474
               class_decl_itr != workspace.class_declaration_indexes.end() &&
20✔
475
               workspace.class_declarations[class_decl_itr->second]->parameters().size() > 0 )
20✔
476
          {
477
            has_base_classes = true;
4✔
478
          }
479

480
          auto msg =
481
              fmt::format( "In function call: Class '{}' {} define a constructor.", class_name,
482
                           has_base_classes ? "and its base class(es) do not" : "does not" );
10✔
483

484
          auto func_itr = workspace.all_function_locations.find(
10✔
485
              ScopableName( class_name, class_name ).string() );
20✔
486

487
          if ( func_itr != workspace.all_function_locations.end() )
10✔
488
          {
489
            msg += fmt::format( "\n  See also (missing 'this' parameter?): {}", func_itr->second );
8✔
490
          }
491

492
          report.error( fc, msg );
10✔
493
          return;  // skip "Unknown identifier" error
10✔
494
        }
10✔
495
      }
496

497
      auto callee = std::make_unique<Identifier>( fc.source_location, *fc.scoped_name );
192✔
498
      fc.children.insert( fc.children.begin(), std::move( callee ) );
192✔
499
      fc.scoped_name.reset();
192✔
500
    }
202✔
501

502
    // For function calls where the callee is not an identifier, take the
503
    // arguments as-is. We don't support named args, as we don't know the
504
    // function to execute until runtime.
505
    auto any_named = std::find_if( fc.children.begin() + 1, fc.children.end(),
464✔
506
                                   []( const std::unique_ptr<Node>& node )
899✔
507
                                   {
508
                                     const auto& arg_name =
509
                                         static_cast<Argument*>( node.get() )->identifier;
899✔
510
                                     return arg_name != nullptr;
899✔
511
                                   } );
512

513
    if ( any_named != fc.children.end() )
464✔
514
    {
515
      report.error( fc, "In function call: Cannot use named arguments here." );
2✔
516

517
      return;
2✔
518
    }
519

520
    visit_children( fc );
462✔
521

522
    return;
462✔
523
  }
524

525
  // here we turn the arguments passed (which can be named or positional)
526
  // into the final_arguments vector, which is just one parameter per
527
  // argument, in the correct order.
528

529
  typedef std::map<std::string, std::unique_ptr<Expression>> ArgumentList;
530
  ArgumentList arguments_passed;
17,392✔
531

532
  typedef std::list<std::unique_ptr<Expression>> VariadicArguments;
533
  VariadicArguments variadic_arguments;
17,392✔
534

535
  bool any_named = false;
17,392✔
536
  auto uf = fc.function_link->user_function();
17,392✔
537

538
  std::vector<std::unique_ptr<Argument>> arguments = fc.take_arguments();
17,392✔
539
  auto parameters = fc.parameters();
17,392✔
540
  bool has_class_inst_parameter = false;
17,392✔
541

542
  bool in_super_func = false;
17,392✔
543
  bool in_constructor_func = false;
17,392✔
544
  bool in_generated_function = false;
17,392✔
545

546
  if ( !user_functions.empty() )
17,392✔
547
  {
548
    if ( user_functions.top()->type == UserFunctionType::Super )
7,380✔
549
    {
550
      in_super_func = true;
239✔
551
    }
552
    else if ( user_functions.top()->type == UserFunctionType::Constructor )
7,141✔
553
    {
554
      in_constructor_func = true;
376✔
555
      in_generated_function = dynamic_cast<GeneratedFunction*>( user_functions.top() );
376✔
556
    }
557
  }
558

559
  if ( uf )
17,392✔
560
  {
561
    // A super() call can only be used in a constructor function.
562
    if ( is_super_call && !in_constructor_func )
4,701✔
563
    {
564
      report.error( fc, "In call to '{}': super() can only be used in constructor functions.",
×
565
                    uf->name );
2✔
566
      return;
2✔
567
    }
568
    // Constructor functions are defined as `Constr( this )` and called
569
    // statically via `Constr()`. Provide a `this` parameter at this function
570
    // call site. Only do this when calling constructors outside of a
571
    // compiler-generated function (ie. super or generated constructor)
572
    else if ( uf->type == UserFunctionType::Constructor && !in_generated_function &&
4,699✔
573
              !in_super_func )
461✔
574
    {
575
      // A super call inherits the `this` argument
576
      if ( is_super_call )
222✔
577
      {
578
        // Super will use "this" argument
579
        arguments.insert( arguments.begin(),
×
580
                          std::make_unique<Argument>(
×
581
                              fc.source_location,
×
582
                              std::make_unique<Identifier>( fc.source_location, "this" ), false ) );
×
583

584
        report.debug( fc, "using ctor Identifier is_super_call={} in_super_func={} uf->name={}",
×
585
                      is_super_call, in_super_func, uf->name );
×
586
      }
587
      else
588
      {
589
        // Should never happen
590
        if ( uf->class_link == nullptr || uf->class_link->class_declaration() == nullptr )
222✔
591
        {
592
          uf->internal_error(
×
593
              fmt::format( "no class declaration found for user function '{}'", uf->name ) );
×
594
        }
595

596
        // Constructor will create a new "this" instance
597
        arguments.insert( arguments.begin(),
222✔
598
                          std::make_unique<Argument>(
222✔
599
                              fc.source_location,
222✔
600
                              std::make_unique<ClassInstance>(
222✔
601
                                  fc.source_location, uf->class_link->class_declaration() ),
222✔
602
                              false ) );
222✔
603

604
        report.debug( fc, "using ClassInstance is_super_call={} in_super_func={} uf->name={}",
222✔
605
                      is_super_call, in_super_func, uf->name );
222✔
606
      }
607
      // Since a `this` argument is generated for constructor functions, disallow passing an
608
      // argument named `this`.
609
      has_class_inst_parameter = true;
222✔
610
    }
611
    else if ( uf->type == UserFunctionType::Super )
4,477✔
612
    {
613
      if ( uf->body().children.empty() )
154✔
614
      {
UNCOV
615
        report.error( fc, "In call to '{}': No base class defines a constructor.", uf->name );
×
UNCOV
616
        return;
×
617
      }
618
      // Super will use "this" argument
619
      arguments.insert( arguments.begin(),
154✔
620
                        std::make_unique<Argument>(
154✔
621
                            fc.source_location,
154✔
622
                            std::make_unique<Identifier>( fc.source_location, "this" ), false ) );
308✔
623

624
      report.debug( fc, "using super Identifier is_super_call={} in_super_func={} uf->name={}",
154✔
625
                    is_super_call, in_super_func, uf->name );
154✔
626
    }
627
  }
628

629
  auto is_callee_variadic = parameters.size() && parameters.back().get().rest;
17,390✔
630

631
  const auto method_name = fc.string();
17,390✔
632

633
  for ( auto& arg_unique_ptr : arguments )
40,008✔
634
  {
635
    auto& arg = *arg_unique_ptr;
22,624✔
636
    std::string arg_name = arg.identifier ? arg.identifier->string() : "";
22,624✔
637

638
    if ( arg.spread )
22,624✔
639
    {
640
      if ( !uf )  // a module function
182✔
641
      {
642
        report.error( arg,
2✔
643
                      "In call to '{}': Spread operator cannot be used in module function call.",
644
                      method_name );
645
        return;
2✔
646
      }
647
      else if ( !uf->is_variadic() )
180✔
648
      {
649
        report.error( arg,
2✔
650
                      "In call to '{}': Spread operator can only be used in variadic functions.",
651
                      method_name );
652
        return;
2✔
653
      }
654
      else if ( arguments_passed.size() < parameters.size() - 1 )
178✔
655
      {
656
        report.error( arg,
2✔
657
                      "In call to '{}': Spread operator can only be used for arguments on or after "
658
                      "the formal rest parameter.",
659
                      method_name );
660
        return;
2✔
661
      }
662
    }
663

664
    if ( arg_name.empty() )
22,618✔
665
    {
666
      // Allow spread elements to come after named arguments, eg:
667
      //
668
      //  `foo( optA := 1, optB := 2, ... c )`
669
      //
670
      if ( any_named && !arg.spread )
21,696✔
671
      {
672
        report.error( arg, "In call to '{}': Unnamed args cannot follow named args.", method_name );
×
673
        return;
×
674
      }
675

676
      // Too many arguments passed?
677
      if ( arguments_passed.size() >= parameters.size() )
21,696✔
678
      {
679
        // Allowed if variadic
680
        if ( is_callee_variadic )
295✔
681
        {
682
          variadic_arguments.push_back( arg.take_expression() );
291✔
683

684
          // Do not add to `arguments_passed`, so continue.
685
          continue;
291✔
686
        }
687
        else
688
        {
689
          auto expected_args =
690
              static_cast<int>( parameters.size() ) - ( has_class_inst_parameter ? 1 : 0 );
4✔
691

692
          report.error( arg, "In call to '{}': Too many arguments passed.  Expected {}, got {}.",
×
693
                        method_name, expected_args, arguments.size() );
4✔
694
          continue;
4✔
695
        }
4✔
696
      }
697
      else
698
      {
699
        arg_name = parameters.at( arguments_passed.size() ).get().name.string();
21,401✔
700
      }
701
    }
702
    else
703
    {
704
      any_named = true;
922✔
705

706
      if ( has_class_inst_parameter && !in_super_func && !is_super_call &&
922✔
707
           Clib::caseInsensitiveEqual( arg_name, "this" ) )
922✔
708
      {
709
        report.error( arg, "In call to '{}': Cannot pass 'this' to constructor function.",
×
710
                      method_name );
711
        return;
×
712
      }
713
    }
714

715
    if ( arguments_passed.find( arg_name ) != arguments_passed.end() )
22,323✔
716
    {
717
      report.error( arg, "In call to '{}': Parameter '{}' passed more than once.", method_name,
×
718
                    arg_name );
719
      return;
×
720
    }
721

722
    // Inside a call to super(), if the arg is un-scoped, find the base class it belongs to. Error
723
    // if ambiguous.
724
    if ( is_super_call )
22,323✔
725
    {
726
      if ( arg.identifier && arg.identifier->global() && arg.identifier->string() != "this" )
338✔
727
      {
728
        std::string base_class;
17✔
729
        std::string first_location;
17✔
730
        std::string err_msg;
17✔
731

732
        auto add_location = [this]( std::string& where, const std::string& class_name )
57✔
733
        {
734
          fmt::format_to( std::back_inserter( where ), "  See: {}", class_name );
19✔
735

736
          auto funct_itr = workspace.all_function_locations.find(
19✔
737
              ScopableName( class_name, class_name ).string() );
38✔
738
          if ( funct_itr != workspace.all_function_locations.end() )
19✔
739
          {
740
            fmt::format_to( std::back_inserter( where ), " {}\n", funct_itr->second );
38✔
741
          }
742
          else
743
          {
744
            where += "\n";
×
745
          }
746
        };
19✔
747

748
        for ( auto& param_ref : parameters )
83✔
749
        {
750
          auto& param = param_ref.get();
66✔
751
          const auto& param_name = param.name.name;
66✔
752
          if ( Clib::caseInsensitiveEqual( param_name, arg_name ) )
66✔
753
          {
754
            if ( !base_class.empty() )
19✔
755
            {
756
              if ( err_msg.empty() )
2✔
757
              {
758
                fmt::format_to( std::back_inserter( err_msg ),
4✔
759
                                "In call to '{}': Ambiguous parameter '{}'.\n{}", method_name,
760
                                param_name, first_location );
761
              }
762

763
              add_location( err_msg, param.name.scope.string() );
2✔
764
            }
765
            else
766
            {
767
              base_class = param.name.scope.string();
17✔
768

769
              add_location( first_location, base_class );
17✔
770
            }
771
          }
772
        }
773
        if ( !err_msg.empty() )
17✔
774
        {
775
          report.error( fc, err_msg );
2✔
776
        }
777
        else
778
        {
779
          arg_name = ScopableName( base_class, arg_name ).string();
15✔
780
        }
781
      }
17✔
782
    }
783

784

785
    arguments_passed[arg_name] = arg.take_expression();
22,323✔
786
  }
22,624✔
787

788
  std::vector<std::unique_ptr<Node>> final_arguments;
17,384✔
789

790
  for ( auto& param_ref : parameters )
50,108✔
791
  {
792
    FunctionParameterDeclaration& param = param_ref.get();
32,738✔
793
    auto itr = arguments_passed.find( param.name.string() );
32,738✔
794
    if ( itr == arguments_passed.end() )
32,738✔
795
    {
796
      if ( auto default_value = param.default_value() )
10,425✔
797
      {
798
        SimpleValueCloner cloner( report, default_value->source_location );
10,384✔
799
        auto final_argument = cloner.clone( *default_value );
10,384✔
800

801
        if ( final_argument )
10,384✔
802
        {
803
          final_arguments.push_back( std::move( final_argument ) );
10,382✔
804
        }
805
        else
806
        {
807
          report.error(
×
808
              param, "In call to '{}': Unable to create argument from default for parameter '{}'.",
809
              method_name, param.name );
2✔
810
          return;
2✔
811
        }
812
      }
10,386✔
813
      else if ( !param.rest )
41✔
814
      {
815
        report.error( fc,
×
816
                      "In call to '{}': Parameter '{}' was not passed, and there is no default.",
817
                      method_name, param.name );
12✔
818
        return;
12✔
819
      }
820
    }
821
    else
822
    {
823
      final_arguments.push_back( std::move( ( *itr ).second ) );
22,313✔
824
      arguments_passed.erase( itr );
22,313✔
825
    }
826
  }
827

828
  if ( is_callee_variadic )
17,370✔
829
  {
830
    // Push the leftover arguments into the call.
831
    for ( auto& arg : variadic_arguments )
538✔
832
    {
833
      final_arguments.push_back( std::move( arg ) );
291✔
834
    }
835
  }
836
  else
837
  {
838
    for ( auto& unused_argument : arguments_passed )
17,125✔
839
    {
840
      report.error( *unused_argument.second,
2✔
841
                    "In call to '{}': Parameter '{}' passed by name, but the function has no "
842
                    "such parameter.",
843
                    method_name, unused_argument.first );
2✔
844
    }
845
    if ( !arguments_passed.empty() || arguments.size() > parameters.size() )
17,123✔
846
      return;
6✔
847
  }
848

849
  fc.children = std::move( final_arguments );
17,364✔
850

851
  // do this afterwards, so that named parameters will not be looked up as identifiers.
852
  visit_children( fc );
17,364✔
853
}
17,522✔
854

855
void SemanticAnalyzer::visit_function_parameter_list( FunctionParameterList& node )
3,170✔
856
{
857
  // A rest parameter can only be the last parameter in the list. Since we
858
  // iterate in reverse, start at `true` and set to `false` after first
859
  // iteration.
860
  bool can_have_rest_parameter = true;
3,170✔
861

862
  for ( auto& child : std::views::reverse( node.children ) )
7,140✔
863
  {
864
    child->accept( *this );
3,970✔
865

866
    bool has_rest_parameter = static_cast<FunctionParameterDeclaration*>( child.get() )->rest;
3,970✔
867

868
    if ( has_rest_parameter && !can_have_rest_parameter )
3,970✔
869
    {
870
      report.error( *child, "Rest parameter must be the last parameter in the list." );
2✔
871
    }
872

873
    can_have_rest_parameter = false;
3,970✔
874
  }
875
}
3,170✔
876

877
void SemanticAnalyzer::visit_function_parameter_declaration( FunctionParameterDeclaration& node )
3,970✔
878
{
879
  auto node_name = node.name.string();
3,970✔
880
  if ( auto default_value = node.default_value() )
3,970✔
881
  {
882
    ConstantValidator validator;
323✔
883
    // By accident, 0-parameter system function calls are allowed as constant values.
884
    // They are not allowed as default parameters, though.
885
    if ( !validator.validate( *default_value ) || dynamic_cast<FunctionCall*>( default_value ) )
323✔
886
    {
887
      report.error( node,
6✔
888
                    "Parameter '{}' has a disallowed default.  Only simple operands are allowed as "
889
                    "default arguments.",
890
                    node_name );
891
      // but continue, to avoid unknown identifier errors
892
    }
893
  }
323✔
894
  if ( auto existing = locals.find( node_name ) )
7,940✔
895
  {
896
    report.error( node, "Parameter '{}' already defined.", node_name );
×
897
    return;
×
898
  }
3,970✔
899

900
  if ( node.rest && node.default_value() )
3,970✔
901
  {
902
    report.error( node, "Rest parameter '{}' cannot have a default value.", node_name );
2✔
903
    return;
2✔
904
  }
905

906
  WarnOn warn_on = node.unused ? WarnOn::IfUsed : WarnOn::IfNotUsed;
3,968✔
907

908
  if ( report_function_name_conflict( node.source_location, node_name, "function parameter" ) )
3,968✔
909
  {
910
    warn_on = WarnOn::Never;
2✔
911
  }
912

913
  local_scopes.current_local_scope()->create( node_name, warn_on, node.source_location );
3,968✔
914
}
3,970✔
915

916
void SemanticAnalyzer::visit_function_expression( FunctionExpression& node )
540✔
917
{
918
  if ( auto user_function = node.function_link->user_function() )
540✔
919
  {
920
    // Create a new capture scope for this function. It must be in a new C++
921
    // scope for to add the captures to
922
    // `user_function->capture_variable_scope_info` via the user function
923
    // visitor.
924
    {
925
      FunctionVariableScope new_capture_scope( captures );
540✔
926
      LocalVariableScope capture_scope( capture_scopes,
540✔
927
                                        user_function->capture_variable_scope_info );
540✔
928
      FunctionVariableScope new_function_scope( locals );
540✔
929
      visit_user_function( *user_function );
540✔
930
    }
540✔
931

932
    // Since the capture_scope was popped (above), any _existing_ capture scope refers to
933
    // the parent function expression in the tree. Adjust that function to inherit this function's
934
    // captures.
935
    if ( auto cap_scope = capture_scopes.current_local_scope() )
540✔
936
    {
937
      for ( auto& variable : user_function->capture_variable_scope_info.variables )
417✔
938
      {
939
        // If the capture is not local, we must capture it
940
        if ( !locals.find( variable->name ) )
604✔
941
        {
942
          // If already captured, set the variables capture to the existing.
943
          if ( auto captured = captures.find( variable->name ) )
354✔
944
          {
945
            variable->capturing = captured;
96✔
946
          }
947
          // Otherwise, create new.
948
          else if ( !captures.find( variable->name ) )
162✔
949
          {
950
            // Create a new capture variable in the parent function, setting
951
            // this function expression's captured variable to this newly
952
            // created one.
953
            variable->capturing = cap_scope->capture( variable->capturing );
81✔
954
          }
177✔
955
        }
956
      }
957
    }
958
  }
959
}
540✔
960

961
void SemanticAnalyzer::visit_function_reference( FunctionReference& node )
738✔
962
{
963
  if ( auto function = node.function_link->user_function() )
738✔
964
  {
965
    if ( function->type == UserFunctionType::Super )
734✔
966
    {
967
      report.error( node, "Cannot reference super() function." );
2✔
968
    }
969
  }
970
  else
971
  {
972
    report.error( node, "User function '{}' not found", node.name );
4✔
973
  }
974
}
738✔
975

976
void SemanticAnalyzer::visit_identifier( Identifier& node )
29,946✔
977
{
978
  // Resolution order:
979
  //
980
  // if scoped: locals -> globals
981
  // otherwise: local function -> local captures -> ancestor (above) functions -> globals
982
  //
983
  const auto& name = node.scoped_name.string();
29,946✔
984

985
  // If there is a scope, whether it is (":foo") empty or not ("Animal:foo"),
986
  // we need to check both globals and locals.
987
  if ( !node.scoped_name.scope.empty() )
29,946✔
988
  {
989
    if ( !node.scoped_name.scope.global() )
282✔
990
    {
991
      if ( auto local = locals.find( name ) )
524✔
992
      {
993
        local->mark_used();
191✔
994
        node.variable = local;
191✔
995
      }
262✔
996
    }
997
    // Did not find it in locals, check globals
998
    if ( !node.variable )
564✔
999
    {
1000
      if ( auto scoped_global = globals.find( name ) )
182✔
1001
      {
1002
        node.variable = scoped_global;
83✔
1003
      }
91✔
1004
    }
1005
  }
1006
  else
1007
  {
1008
    if ( auto local = locals.find( name ) )
59,328✔
1009
    {
1010
      local->mark_used();
20,237✔
1011
      node.variable = local;
20,237✔
1012
    }
1013
    else if ( auto captured = captures.find( name ) )
18,854✔
1014
    {
1015
      // Should already be marked used as it's not newly created (done below).
1016
      // There is no `captures.find_in_ancestors()` check because if an upper
1017
      // capture was found, we still need to capture it for our own function (done
1018
      // below).
1019
      node.variable = captured;
186✔
1020
    }
1021
    else if ( auto ancestor = locals.find_in_ancestors( name ) )
18,482✔
1022
    {
1023
      // Capture the variable. In a deeply nested capture, this will reference the
1024
      // local in the ancestor function. The function expression visitor will swap
1025
      // the 'capturing' to a local-safe variable.
1026
      node.variable = capture_scopes.current_local_scope()->capture( ancestor );
312✔
1027
      node.variable->mark_used();
312✔
1028
    }
1029
    else if ( auto global = globals.find( name ) )
17,858✔
1030
    {
1031
      node.variable = global;
8,889✔
1032
    }
1033
    else if ( !current_scope_name().global() )
40✔
1034
    {
1035
      const auto scoped_name = ScopableName( current_scope_name(), node.name() ).string();
12✔
1036

1037
      // We do not support nested classes, so if there is a `current_scope`, it would only _ever_
1038
      // exist in globals.
1039
      if ( auto scoped_global = globals.find( scoped_name ) )
24✔
1040
      {
1041
        node.variable = scoped_global;
12✔
1042
      }
12✔
1043
    }
57,273✔
1044
  }
1045

1046
  if ( !node.variable )
59,892✔
1047
  {
1048
    report.error( node, "Unknown identifier '{}'.", name );
36✔
1049
    return;
36✔
1050
  }
1051
}
29,946✔
1052

1053
void SemanticAnalyzer::visit_variable_binding( VariableBinding& node )
762✔
1054
{
1055
  if ( auto variable = create_variable( node.source_location, node.scoped_name.scope.string(),
762✔
1056
                                        node.scoped_name.name ) )
1,524✔
1057
  {
1058
    node.variable = std::move( variable );
762✔
1059
    visit_children( node );
762✔
1060
  }
762✔
1061
}
762✔
1062

1063
void SemanticAnalyzer::visit_jump_statement( JumpStatement& node )
643✔
1064
{
1065
  auto& scopes = node.jump_type == JumpStatement::Break ? break_scopes : continue_scopes;
643✔
1066

1067
  if ( auto scope = scopes.find( node.label ) )
643✔
1068
  {
1069
    node.flow_control_label = scope->flow_control_label;
643✔
1070
    node.local_variables_to_remove = locals.count() - scope->local_variables_size;
643✔
1071
  }
1072
  else
1073
  {
1074
    auto type_str = node.jump_type == JumpStatement::Break ? "break" : "continue";
×
1075

1076
    if ( !node.label.empty() && break_scopes.any() )
×
1077
      report.error( node, "Label '{}' not found for {}", node.label, type_str );
×
1078
    else
1079
      report.error( node, "Cannot {} here.", type_str );
×
1080
  }
1081
}
643✔
1082

1083

1084
void SemanticAnalyzer::visit_loop_statement( LoopStatement& loop )
531✔
1085
{
1086
  FlowControlScope continue_scope( continue_scopes, loop.source_location, loop.get_label(),
531✔
1087
                                   loop.continue_label );
1,062✔
1088
  FlowControlScope break_scope( break_scopes, loop.source_location, loop.get_label(),
531✔
1089
                                loop.break_label );
1,062✔
1090

1091
  visit_children( loop );
531✔
1092
}
531✔
1093

1094
void SemanticAnalyzer::visit_program( Program& program )
450✔
1095
{
1096
  LocalVariableScope scope( local_scopes, program.local_variable_scope_info );
450✔
1097

1098
  visit_children( program );
450✔
1099
}
450✔
1100

1101
void SemanticAnalyzer::visit_program_parameter_declaration( ProgramParameterDeclaration& node )
208✔
1102
{
1103
  if ( auto existing = locals.find( node.name ) )
416✔
1104
  {
1105
    report.error( node, "Parameter '{}' already defined.", node.name );
×
1106
    return;
×
1107
  }
208✔
1108
  WarnOn warn_on = node.unused ? WarnOn::IfUsed : WarnOn::IfNotUsed;
208✔
1109

1110
  if ( report_function_name_conflict( node.source_location, node.name, "program parameter" ) )
208✔
1111
  {
1112
    warn_on = WarnOn::Never;
2✔
1113
  }
1114

1115
  local_scopes.current_local_scope()->create( node.name, warn_on, node.source_location );
208✔
1116
}
1117

1118
void SemanticAnalyzer::visit_repeat_until_loop( RepeatUntilLoop& node )
38✔
1119
{
1120
  visit_loop_statement( node );
38✔
1121
}
38✔
1122

1123
void SemanticAnalyzer::visit_return_statement( ReturnStatement& node )
4,497✔
1124
{
1125
  if ( !user_functions.empty() )
4,497✔
1126
  {
1127
    auto uf = user_functions.top();
4,193✔
1128

1129
    if ( uf->type == UserFunctionType::Constructor && !node.children.empty() )
4,193✔
1130
    {
1131
      report.error( node, "Cannot return a value from a constructor function." );
2✔
1132
    }
1133
  }
1134

1135
  visit_children( node );
4,497✔
1136
}
4,497✔
1137

1138
void SemanticAnalyzer::visit_sequence_binding( SequenceBinding& node )
111✔
1139
{
1140
  u8 index = 0;
111✔
1141
  VariableBinding* previous_rest_binding = nullptr;
111✔
1142

1143
  if ( node.binding_count() > 127 )
111✔
1144
  {
1145
    report.error( node, "Too many binding elements. Maximum is 127." );
×
1146
  }
1147

1148
  for ( const auto& child : node.children )
445✔
1149
  {
1150
    if ( auto index_binding = dynamic_cast<VariableBinding*>( child.get() ) )
334✔
1151
    {
1152
      if ( index_binding->rest )
319✔
1153
      {
1154
        if ( previous_rest_binding != nullptr )
73✔
1155
        {
1156
          report.error( *index_binding,
2✔
1157
                        "Only one rest binding is allowed.\n"
1158
                        "  See also: {}",
1159
                        previous_rest_binding->source_location );
2✔
1160
        }
1161

1162
        previous_rest_binding = index_binding;
73✔
1163
        node.rest_index = index;
73✔
1164
      }
1165
    }
1166

1167
    ++index;
334✔
1168
  }
1169

1170
  visit_children( node );
111✔
1171
}
111✔
1172

1173
void SemanticAnalyzer::visit_user_function( UserFunction& node )
3,170✔
1174
{
1175
  // Track current scope for use in visit_identifier
1176
  current_scope_names.push( ScopeName( node.scope ) );
3,170✔
1177
  user_functions.emplace( &node );
3,170✔
1178
  if ( node.exported )
3,170✔
1179
  {
1180
    if ( !node.scope.empty() )
557✔
1181
    {
1182
      report.error( node, "Exported function '{}' cannot be scoped.", node.scoped_name() );
2✔
1183
    }
1184
    else
1185
    {
1186
      unsigned max_name_length = sizeof( Pol::Bscript::BSCRIPT_EXPORTED_FUNCTION::funcname ) - 1;
555✔
1187
      if ( node.name.length() > max_name_length )
555✔
1188
      {
1189
        report.error( node,
×
1190
                      "Exported function name '{}' is too long at {} characters.  Max length: {}",
1191
                      node.name, node.name.length(), max_name_length );
2✔
1192
      }
1193
    }
1194
  }
1195

1196
  LocalVariableScope scope( local_scopes, node.local_variable_scope_info );
3,170✔
1197
  visit_children( node );
3,170✔
1198
  user_functions.pop();
3,170✔
1199
  current_scope_names.pop();
3,170✔
1200
}
3,170✔
1201

1202
void SemanticAnalyzer::visit_var_statement( VarStatement& node )
5,606✔
1203
{
1204
  if ( auto variable = create_variable( node.source_location, node.scope, node.name ) )
11,212✔
1205
  {
1206
    node.variable = std::move( variable );
5,602✔
1207
    visit_children( node );
5,602✔
1208
  }
5,606✔
1209
}
5,606✔
1210

1211
void SemanticAnalyzer::visit_variable_assignment_statement( VariableAssignmentStatement& node )
1,907✔
1212
{
1213
  visit_children( node );
1,907✔
1214

1215
  if ( auto bop = dynamic_cast<BinaryOperator*>( &node.rhs() ) )
1,907✔
1216
  {
1217
    if ( bop->token_id == TOK_ASSIGN )
177✔
1218
    {
1219
      if ( auto second_ident = dynamic_cast<Identifier*>( &bop->lhs() ) )
3✔
1220
      {
1221
        if ( node.identifier().variable == second_ident->variable )
×
1222
        {
1223
          // we have something like
1224
          //      a := a := expr;
1225
          report.warning( node, "Double-assignment to the same variable '{}'.",
×
1226
                          node.identifier().name() );
×
1227
        }
1228
      }
1229
    }
1230
  }
1231
}
1,907✔
1232

1233
void SemanticAnalyzer::visit_while_loop( WhileLoop& node )
193✔
1234
{
1235
  visit_loop_statement( node );
193✔
1236
}
193✔
1237

1238
void SemanticAnalyzer::visit_constant_loop( ConstantPredicateLoop& node )
224✔
1239
{
1240
  visit_loop_statement( node );
224✔
1241
}
224✔
1242

1243
std::shared_ptr<Variable> SemanticAnalyzer::create_variable( const SourceLocation& source_location,
6,368✔
1244
                                                             const std::string& scope,
1245
                                                             const std::string& name )
1246
{
1247
  auto maybe_scoped_name = ScopableName( scope, name ).string();
6,368✔
1248

1249
  // Since this is not scoped check, we cannot have `Animal::FOO` and a constant `FOO`.
1250
  if ( auto constant = workspace.constants.find( name ) )
6,368✔
1251
  {
1252
    report.error( source_location,
×
1253
                  "Cannot define a variable with the same name as constant '{}'.\n"
1254
                  "  See also: {}",
1255
                  name, constant->source_location );
4✔
1256
    return {};
4✔
1257
  }
1258

1259
  report_function_name_conflict( source_location, maybe_scoped_name, "variable" );
6,364✔
1260

1261
  if ( auto local_scope = local_scopes.current_local_scope() )
6,364✔
1262
  {
1263
    return local_scope->create( maybe_scoped_name, WarnOn::Never, source_location );
3,769✔
1264
  }
1265
  else
1266
  {
1267
    if ( auto existing = globals.find( maybe_scoped_name ) )
5,190✔
1268
    {
1269
      report.error( source_location,
×
1270
                    "Global variable '{}' already defined.\n"
1271
                    "  See also: {}",
1272
                    maybe_scoped_name, existing->source_location );
×
1273
      return {};
×
1274
    }
2,595✔
1275

1276
    return globals.create( maybe_scoped_name, 0, WarnOn::Never, source_location );
2,595✔
1277
  }
1278
}
6,368✔
1279

1280
bool SemanticAnalyzer::report_function_name_conflict( const SourceLocation& referencing_loc,
11,079✔
1281
                                                      const std::string& function_name,
1282
                                                      const std::string& element_description )
1283
{
1284
  return report_function_name_conflict(
22,158✔
1285
      workspace, report, referencing_loc,
11,079✔
1286
      ScopableName( current_scope_name(), function_name ).string(), element_description );
22,158✔
1287
}
1288

1289
bool SemanticAnalyzer::report_function_name_conflict( const CompilerWorkspace& workspace,
167,985✔
1290
                                                      Report& report,
1291
                                                      const SourceLocation& referencing_loc,
1292
                                                      const std::string& function_name,
1293
                                                      const std::string& element_description )
1294
{
1295
  auto func_itr = workspace.all_function_locations.find( function_name );
167,985✔
1296
  if ( func_itr != workspace.all_function_locations.end() )
167,985✔
1297
  {
1298
    const SourceLocation& function_loc = ( *func_itr ).second;
16✔
1299
    report.error( referencing_loc,
16✔
1300
                  "Cannot define a {} with the same name as function '{}'.\n"
1301
                  "  Defined here: {}",
1302
                  element_description, function_name, function_loc );
1303
    return true;
16✔
1304
  }
1305
  return false;
167,969✔
1306
}
1307

1308
}  // 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