• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

polserver / polserver / 16683711876

01 Aug 2025 07:40PM UTC coverage: 59.756% (-0.03%) from 59.79%
16683711876

push

github

web-flow
use c++20 (#799)

* use c++20
* increased used clang version
* compiler report and logfacility use now compile time formatting, which
means that the formatstring gets checked at compile time. (which found 2 errors)
adapted a few places since report only accepts formatting arguments
adapted a few places with logging since char[] is compile time
formatting and string or chr* is runtime formatting
* use std::ranges instead of boost
* disabled pragma_assume vs specific macro (I guess noone cares)
* needed to fix ancient ms exception code
* modernized SpinLock
* removed unused code in ECompile
* replaced std::filesystem::path::u8string with string. It now returns an actual u8string type
* cleanup layers.h added the defines for other layers which where before
  only defined in the pkt
* osmod::OpenConnection and HTTPRequest cleanup: early outs, dont check for pChild which is
  only needed for startscript and placed suspend at the very last
  position

* fix warning

* rebuild cache with new compiler version

* define c++ standard for external libs where possible

* added fixme

156 of 212 new or added lines in 26 files covered. (73.58%)

45 existing lines in 19 files now uncovered.

43621 of 72999 relevant lines covered (59.76%)

409874.73 hits per line

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

92.46
/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/Variable.h"
61
#include "bscript/compiler/optimizer/ConstantValidator.h"
62
#include "clib/strutil.h"
63
#include "filefmt.h"
64

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

81
SemanticAnalyzer::~SemanticAnalyzer() = default;
2,217✔
82

83
void SemanticAnalyzer::register_const_declarations( CompilerWorkspace& workspace, Report& report )
2,229✔
84
{
85
  for ( auto& constant : workspace.const_declarations )
156,635✔
86
  {
87
    report_function_name_conflict( workspace, report, constant->source_location,
308,812✔
88
                                   constant->identifier, "constant" );
154,406✔
89
    workspace.constants.create( *constant );
154,406✔
90
  }
91
}
2,229✔
92

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

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

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

117
  workspace.global_variable_names = globals.get_names();
2,217✔
118
}
2,217✔
119

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

132
  node.first().accept( *this );
65✔
133
  node.last().accept( *this );
65✔
134

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

139
  FlowControlScope break_scope( break_scopes, node.source_location, node.get_label(),
65✔
140
                                node.break_label );
130✔
141
  FlowControlScope continue_scope( continue_scopes, node.source_location, node.get_label(),
65✔
142
                                   node.continue_label );
130✔
143

144
  node.block().accept( *this );
65✔
145
}
65✔
146

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

151
  visit_children( block );
5,731✔
152
}
5,731✔
153

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

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

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

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

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

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

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

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

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

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

215
    bool previously_referenced =
216
        named_baseclasses.find( baseclass_name ) != named_baseclasses.end();
302✔
217

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

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

241
    visited.insert( cd );
347✔
242

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

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

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

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

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

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

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

320
private:
321
  Report& report;
322

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

328

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

334
  FlowControlScope break_scope( break_scopes, case_ast.source_location, case_ast.get_label(),
107✔
335
                                case_ast.break_label );
214✔
336

337
  visit_children( case_ast );
107✔
338
}
107✔
339

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

345
  visit_children( dispatch_group );
339✔
346
}
339✔
347

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

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

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

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

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

374
  CaseDispatchSelectorAnalyzer selector_analyzer( report );
339✔
375
  selectors.accept( selector_analyzer );
339✔
376
}
339✔
377

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

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

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

396
  node.expression().accept( *this );
464✔
397

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

403
  FlowControlScope break_scope( break_scopes, node.source_location, node.get_label(),
464✔
404
                                node.break_label );
928✔
405
  FlowControlScope continue_scope( continue_scopes, node.source_location, node.get_label(),
464✔
406
                                   node.continue_label );
928✔
407

408
  node.block().accept( *this );
464✔
409
}
464✔
410

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

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

423
      ( !fc.function_link->function() &&  // no linked function
17,935✔
424
        fc.scoped_name &&                 // there is a name in the call (ie. not an expression)
469✔
425
        Clib::caseInsensitiveEqual( fc.scoped_name->string(), "super" ) );  // the name is "super"
17,821✔
426

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

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

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

445
        class_itr = workspace.all_class_locations.find( class_name );
191✔
446
        if ( class_itr == workspace.all_class_locations.end() )
191✔
447
        {
448
          class_name.clear();
183✔
449
        }
450
      }
451

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

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

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

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

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

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

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

498
      return;
2✔
499
    }
500

501
    visit_children( fc );
467✔
502

503
    return;
467✔
504
  }
505

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

510
  typedef std::map<std::string, std::unique_ptr<Expression>> ArgumentList;
511
  ArgumentList arguments_passed;
17,155✔
512

513
  typedef std::list<std::unique_ptr<Expression>> VariadicArguments;
514
  VariadicArguments variadic_arguments;
17,155✔
515

516
  bool any_named = false;
17,155✔
517
  auto uf = fc.function_link->user_function();
17,155✔
518

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

523
  bool in_super_func = false;
17,155✔
524
  bool in_constructor_func = false;
17,155✔
525
  bool in_generated_function = false;
17,155✔
526

527
  if ( !user_functions.empty() )
17,155✔
528
  {
529
    if ( user_functions.top()->type == UserFunctionType::Super )
7,265✔
530
    {
531
      in_super_func = true;
239✔
532
    }
533
    else if ( user_functions.top()->type == UserFunctionType::Constructor )
7,026✔
534
    {
535
      in_constructor_func = true;
351✔
536
      in_generated_function = dynamic_cast<GeneratedFunction*>( user_functions.top() );
351✔
537
    }
538
  }
539

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

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

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

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

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

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

619
  auto is_callee_variadic = parameters.size() && parameters.back().get().rest;
17,149✔
620

621
  const auto method_name = fc.string();
17,149✔
622

623
  for ( auto& arg_unique_ptr : arguments )
39,541✔
624
  {
625
    auto& arg = *arg_unique_ptr;
22,398✔
626
    std::string arg_name = arg.identifier ? arg.identifier->string() : "";
22,398✔
627

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

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

666
      // Too many arguments passed?
667
      if ( arguments_passed.size() >= parameters.size() )
21,482✔
668
      {
669
        // Allowed if variadic
670
        if ( is_callee_variadic )
295✔
671
        {
672
          variadic_arguments.push_back( arg.take_expression() );
291✔
673

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

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

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

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

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

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

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

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

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

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

774

775
    arguments_passed[arg_name] = arg.take_expression();
22,097✔
776
  }
22,398✔
777

778
  std::vector<std::unique_ptr<Node>> final_arguments;
17,143✔
779

780
  for ( auto& param_ref : parameters )
49,493✔
781
  {
782
    FunctionParameterDeclaration& param = param_ref.get();
32,364✔
783
    auto itr = arguments_passed.find( param.name.string() );
32,364✔
784
    if ( itr == arguments_passed.end() )
32,364✔
785
    {
786
      if ( auto default_value = param.default_value() )
10,277✔
787
      {
788
        SimpleValueCloner cloner( report, default_value->source_location );
10,236✔
789
        auto final_argument = cloner.clone( *default_value );
10,236✔
790

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

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

839
  fc.children = std::move( final_arguments );
17,123✔
840

841
  // do this afterwards, so that named parameters will not be looked up as identifiers.
842
  visit_children( fc );
17,123✔
843
}
17,297✔
844

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

852
  for ( auto& child : std::views::reverse( node.children ) )
6,976✔
853
  {
854
    child->accept( *this );
3,892✔
855

856
    bool has_rest_parameter = static_cast<FunctionParameterDeclaration*>( child.get() )->rest;
3,892✔
857

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

863
    can_have_rest_parameter = false;
3,892✔
864
  }
865
}
3,084✔
866

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

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

896
  WarnOn warn_on = node.unused ? WarnOn::IfUsed : WarnOn::IfNotUsed;
3,890✔
897

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

903
  local_scopes.current_local_scope()->create( node_name, warn_on, node.source_location );
3,890✔
904
}
3,892✔
905

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

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

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

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

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

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

1036
  if ( !node.variable )
59,216✔
1037
  {
1038
    report.error( node, "Unknown identifier '{}'.", name );
40✔
1039
    return;
40✔
1040
  }
1041
}
29,608✔
1042

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

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

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

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

1073

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

1081
  visit_children( loop );
531✔
1082
}
531✔
1083

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

1088
  visit_children( program );
450✔
1089
}
450✔
1090

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

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

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

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

1113
void SemanticAnalyzer::visit_return_statement( ReturnStatement& node )
4,464✔
1114
{
1115
  if ( !user_functions.empty() )
4,464✔
1116
  {
1117
    auto uf = user_functions.top();
4,160✔
1118

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

1125
  visit_children( node );
4,464✔
1126
}
4,464✔
1127

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

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

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

1152
        previous_rest_binding = index_binding;
73✔
1153
        node.rest_index = index;
73✔
1154
      }
1155
    }
1156

1157
    ++index;
334✔
1158
  }
1159

1160
  visit_children( node );
111✔
1161
}
111✔
1162

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

1186
  LocalVariableScope scope( local_scopes, node.local_variable_scope_info );
3,084✔
1187
  visit_children( node );
3,084✔
1188
  user_functions.pop();
3,084✔
1189
  current_scope_names.pop();
3,084✔
1190
}
3,084✔
1191

1192
void SemanticAnalyzer::visit_var_statement( VarStatement& node )
5,564✔
1193
{
1194
  if ( auto variable = create_variable( node.source_location, node.scope, node.name ) )
11,128✔
1195
  {
1196
    node.variable = std::move( variable );
5,560✔
1197
    visit_children( node );
5,560✔
1198
  }
5,564✔
1199
}
5,564✔
1200

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

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

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

1228
void SemanticAnalyzer::visit_constant_loop( ConstantPredicateLoop& node )
224✔
1229
{
1230
  visit_loop_statement( node );
224✔
1231
}
224✔
1232

1233
std::shared_ptr<Variable> SemanticAnalyzer::create_variable( const SourceLocation& source_location,
6,326✔
1234
                                                             const std::string& scope,
1235
                                                             const std::string& name )
1236
{
1237
  auto maybe_scoped_name = ScopableName( scope, name ).string();
6,326✔
1238

1239
  // Since this is not scoped check, we cannot have `Animal::FOO` and a constant `FOO`.
1240
  if ( auto constant = workspace.constants.find( name ) )
6,326✔
1241
  {
UNCOV
1242
    report.error( source_location,
×
1243
                  "Cannot define a variable with the same name as constant '{}'.\n"
1244
                  "  See also: {}",
1245
                  name, constant->source_location );
4✔
1246
    return {};
4✔
1247
  }
1248

1249
  report_function_name_conflict( source_location, maybe_scoped_name, "variable" );
6,322✔
1250

1251
  if ( auto local_scope = local_scopes.current_local_scope() )
6,322✔
1252
  {
1253
    return local_scope->create( maybe_scoped_name, WarnOn::Never, source_location );
3,742✔
1254
  }
1255
  else
1256
  {
1257
    if ( auto existing = globals.find( maybe_scoped_name ) )
5,160✔
1258
    {
1259
      report.error( source_location,
×
1260
                    "Global variable '{}' already defined.\n"
1261
                    "  See also: {}",
1262
                    maybe_scoped_name, existing->source_location );
×
1263
      return {};
×
1264
    }
2,580✔
1265

1266
    return globals.create( maybe_scoped_name, 0, WarnOn::Never, source_location );
2,580✔
1267
  }
1268
}
6,326✔
1269

1270
bool SemanticAnalyzer::report_function_name_conflict( const SourceLocation& referencing_loc,
10,953✔
1271
                                                      const std::string& function_name,
1272
                                                      const std::string& element_description )
1273
{
1274
  return report_function_name_conflict(
21,906✔
1275
      workspace, report, referencing_loc,
10,953✔
1276
      ScopableName( current_scope_name(), function_name ).string(), element_description );
21,906✔
1277
}
1278

1279
bool SemanticAnalyzer::report_function_name_conflict( const CompilerWorkspace& workspace,
165,359✔
1280
                                                      Report& report,
1281
                                                      const SourceLocation& referencing_loc,
1282
                                                      const std::string& function_name,
1283
                                                      const std::string& element_description )
1284
{
1285
  auto func_itr = workspace.all_function_locations.find( function_name );
165,359✔
1286
  if ( func_itr != workspace.all_function_locations.end() )
165,359✔
1287
  {
1288
    const SourceLocation& function_loc = ( *func_itr ).second;
16✔
1289
    report.error( referencing_loc,
16✔
1290
                  "Cannot define a {} with the same name as function '{}'.\n"
1291
                  "  Defined here: {}",
1292
                  element_description, function_name, function_loc );
1293
    return true;
16✔
1294
  }
1295
  return false;
165,343✔
1296
}
1297

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