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

polserver / polserver / 21108840797

18 Jan 2026 08:35AM UTC coverage: 60.508% (+0.02%) from 60.492%
21108840797

push

github

web-flow
ClangTidy readability-else-after-return (#857)

* trigger tidy

* Automated clang-tidy change: readability-else-after-return

* compile test

* rerun

* Automated clang-tidy change: readability-else-after-return

* trigger..

* Automated clang-tidy change: readability-else-after-return

* manually removed a few

* Automated clang-tidy change: readability-else-after-return

* removed duplicate code

* fix remaining warnings

* fixed scope

---------

Co-authored-by: Clang Tidy <clang-tidy@users.noreply.github.com>

837 of 1874 new or added lines in 151 files covered. (44.66%)

46 existing lines in 25 files now uncovered.

44448 of 73458 relevant lines covered (60.51%)

525066.38 hits per line

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

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

3
#include <algorithm>
4
#include <iterator>
5
#include <list>
6
#include <ranges>
7
#include <set>
8

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

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

85
SemanticAnalyzer::~SemanticAnalyzer() = default;
2,395✔
86

87
void SemanticAnalyzer::register_const_declarations( CompilerWorkspace& workspace, Report& report )
2,407✔
88
{
89
  for ( auto& constant : workspace.const_declarations )
171,288✔
90
  {
91
    report_function_name_conflict( workspace, report, constant->source_location,
506,643✔
92
                                   constant->name.string(), "constant" );
337,762✔
93
    workspace.constants.create( *constant );
168,881✔
94
  }
95
}
2,407✔
96

97
void SemanticAnalyzer::analyze()
2,395✔
98
{
99
  workspace.top_level_statements->accept( *this );
2,395✔
100
  if ( auto& program = workspace.program )
2,395✔
101
  {
102
    program->accept( *this );
452✔
103
  }
104

105
  for ( auto& user_function : workspace.user_functions )
5,916✔
106
  {
107
    // Function expressions are analyzed within visit_function_expression.
108
    if ( !user_function->expression )
3,521✔
109
    {
110
      user_function->accept( *this );
2,846✔
111
    }
112
  }
113

114
  // Class declarations do not have var statements as children, so we do not get
115
  // 'duplicate variable' errors.
116
  for ( auto& class_decl : workspace.class_declarations )
3,078✔
117
  {
118
    class_decl->accept( *this );
683✔
119
  }
120

121
  workspace.global_variable_names = globals.get_names();
2,395✔
122
}
2,395✔
123

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

136
  node.first().accept( *this );
68✔
137
  node.last().accept( *this );
68✔
138

139
  LocalVariableScope scope( local_scopes, node.local_variable_scope_info );
68✔
140
  scope.create( node.identifier, WarnOn::Never, node.source_location );
68✔
141
  scope.create( "_" + node.identifier + "_end", WarnOn::Never, node.source_location );
68✔
142

143
  FlowControlScope break_scope( break_scopes, node.source_location, node.get_label(),
68✔
144
                                node.break_label );
136✔
145
  FlowControlScope continue_scope( continue_scopes, node.source_location, node.get_label(),
68✔
146
                                   node.continue_label );
136✔
147

148
  node.block().accept( *this );
68✔
149
}
68✔
150

151
void SemanticAnalyzer::visit_block( Block& block )
6,043✔
152
{
153
  LocalVariableScope scope( local_scopes, block.local_variable_scope_info );
6,043✔
154

155
  visit_children( block );
6,043✔
156
}
6,043✔
157

158
void SemanticAnalyzer::visit_index_binding( IndexBinding& node )
92✔
159
{
160
  u8 index = 0;
92✔
161

162
  if ( node.binding_count() > 127 )
92✔
163
  {
164
    report.error( node, "Too many binding elements. Maximum is 127." );
2✔
165
  }
166

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

176
        node.rest_index = index;
29✔
177
      }
178
    }
179

180
    ++index;
476✔
181
  }
92✔
182

183
  visit_children( node );
92✔
184
}
92✔
185

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

193
  report.debug( node, "Class '{}' declared with {} parameters", class_name,
×
194
                node.parameters().size() );
683✔
195

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

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

219
    bool previously_referenced =
220
        named_baseclasses.find( baseclass_name ) != named_baseclasses.end();
437✔
221

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

236
  for ( auto to_visit_itr = to_visit.begin(); to_visit_itr != to_visit.end();
1,235✔
237
        to_visit_itr = to_visit.erase( to_visit_itr ) )
552✔
238
  {
239
    auto cd = *to_visit_itr;
552✔
240
    if ( visited.find( cd ) != visited.end() )
552✔
241
    {
242
      continue;
53✔
243
    }
244

245
    visited.insert( cd );
499✔
246

247
    if ( cd == &node )
499✔
248
    {
249
      report.error( node, "Class '{}' references itself as a base class through inheritance.",
8✔
250
                    class_name );
251
    }
252

253
    for ( const auto& base_class_link : cd->base_class_links )
620✔
254
    {
255
      if ( auto base_cd = base_class_link->class_declaration() )
121✔
256
      {
257
        to_visit.push_back( base_cd );
119✔
258
      }
259
      // Do not error, as the the (middle) parent class which referenced the
260
      // (top) parent class would have already errored in the previous for-loop.
261
    }
262
  }
263

264
  for ( const auto& uninit_function_ref : node.uninit_functions() )
780✔
265
  {
266
    const auto& uninit_function = uninit_function_ref.get();
97✔
267
    if ( auto exiting_method_itr = node.methods.find( uninit_function.name );
97✔
268
         exiting_method_itr != node.methods.end() )
97✔
269
    {
270
      report.error( uninit_function.source_location,
2✔
271
                    "In uninitialized function declaration: A method named '{}' is already "
272
                    "defined in class '{}'.\n"
273
                    "  See also: {}",
274
                    uninit_function.name, class_name, exiting_method_itr->second->source_location );
2✔
275
    }
276
    else if ( uninit_function.type == UserFunctionType::Constructor && node.constructor_link )
108✔
277
    {
278
      report.error(
2✔
279
          uninit_function.source_location,
2✔
280
          "In uninitialized function declaration: A constructor is already defined in class '{}'.\n"
281
          "  See also: {}",
282
          class_name, node.constructor_link->source_location );
2✔
283
    }
284
  }
683✔
285

286
  // To visit UninitializedFunctionDeclarations
287
  visit_children( node );
683✔
288
}
683✔
289

290
void SemanticAnalyzer::visit_uninitialized_function_declaration(
97✔
291
    UninitializedFunctionDeclaration& node )
292
{
293
  if ( Clib::caseInsensitiveEqual( node.name, Compiler::SUPER ) )
97✔
294
  {
295
    report.error( node, "An uninitialized function cannot be named 'super'." );
2✔
296
  }
297
  else if ( node.type == UserFunctionType::Static )
95✔
298
  {
299
    report.error( node.source_location,
2✔
300
                  "In uninitialized function declaration: Static functions cannot be "
301
                  "marked as uninitialized." );
302
  }
303
  else
304
  {
305
    // Cannot use visit_children to visit the parameters, since the the
306
    // SemanticAnalyzer would attempt to make variables for the function inside
307
    // visit_function_parameter_list.
308

309
    bool can_have_rest_parameter = true;
93✔
310
    bool can_have_defaults = true;
93✔
311

312
    auto params = node.parameters();
93✔
313

314
    // Rest params must be last, and defaulted params must come after
315
    // non-defaulted params (excluding rest param).
316
    for ( auto& param_ref : std::views::reverse( params ) )
276✔
317
    {
318
      auto& param = param_ref.get();
183✔
319

320
      if ( param.rest )
183✔
321
      {
322
        if ( !can_have_rest_parameter )
10✔
323
        {
324
          report.error( param,
2✔
325
                        "In uninitialized function declaration: Rest parameter must be the last "
326
                        "parameter in the list." );
327
        }
328
        else if ( param.uninit_default )
8✔
329
        {
330
          report.error( param.source_location,
2✔
331
                        "In uninitialized function declaration: Rest parameter cannot have a "
332
                        "default value." );
333
        }
334
      }
335
      else if ( param.uninit_default )
173✔
336
      {
337
        if ( !can_have_defaults )
13✔
338
        {
339
          report.error( param,
2✔
340
                        "In uninitialized function declaration: Parameters with default values "
341
                        "must come after all parameters without default values." );
342
        }
343
      }
344
      else
345
      {
346
        can_have_defaults = false;
160✔
347
      }
348

349
      can_have_rest_parameter = false;
183✔
350
    }
351
  }
93✔
352
}
97✔
353

354
void SemanticAnalyzer::analyze_class( ClassDeclaration* class_decl )
468✔
355
{
356
  if ( analyzed_classes.contains( class_decl ) )
468✔
357
    return;
125✔
358

359
  // Since only non-static classes (ie. those with constructors) can have uninitialized
360
  // functions, skip analysis if the class does not have a (possibly inherited) constructor.
361
  //
362
  // The check for `user_function()` is just a safety check to prevent null pointer dereference:
363
  // other checks would report on this not being linked.
364
  if ( !class_decl->constructor_link || !class_decl->constructor_link->user_function() )
686✔
365
  {
366
    analyzed_classes.insert( class_decl );
×
367
    return;
×
368
  }
369

370
  std::vector<std::reference_wrapper<UninitializedFunctionDeclaration>> all_uninit_functions;
343✔
371
  std::list<ClassDeclaration*> to_visit{ class_decl };
343✔
372
  std::set<ClassDeclaration*> visited;
343✔
373
  std::map<std::string, UserFunction*, Clib::ci_cmp_pred> all_methods;
343✔
374

375
  for ( auto* cd : to_visit )
1,158✔
376
  {
377
    if ( visited.contains( cd ) )
815✔
378
    {
379
      continue;
49✔
380
    }
381

382
    visited.insert( cd );
766✔
383

384
    auto cd_uninit_functions = cd->uninit_functions();
766✔
385
    std::ranges::move( cd_uninit_functions, std::back_inserter( all_uninit_functions ) );
766✔
386

387
    for ( const auto& [method_name, method_link] : cd->methods )
1,061✔
388
    {
389
      if ( auto uf = method_link->user_function() )
295✔
390
      {
391
        // Only add the method if it doesn't already exist: a base class' method
392
        // should not overwrite a child class' method.
393
        if ( !all_methods.contains( method_name ) )
295✔
394
        {
395
          all_methods[method_name] = uf;
241✔
396
        }
397
      }
398
      else
399
      {
400
        // Should never happen, as a method function link is only created if
401
        // there was a FunctionDeclarationContext to visit inside
402
        // UserFunctionBuilder, and the function link is immediately registered
403
        // with the FunctionResolver, guaranteeing it will be visited/built.
404
        cd->internal_error( fmt::format( "no user function linked for method {}::{}",
×
405
                                         class_decl->name, method_name ) );
×
406
      }
407
    }
408

409
    for ( const auto& base_class_link : cd->base_class_links )
1,240✔
410
    {
411
      if ( auto base_cd = base_class_link->class_declaration() )
474✔
412
      {
413
        to_visit.push_back( base_cd );
472✔
414
      }
415
      // Do not error if no ClassDeclaration found, as it will be reported in
416
      // visit_class_declaration.
417
    }
418
  }
766✔
419

420
  auto report_error_if_not_same =
421
      [&]( UserFunction* defined_func, UninitializedFunctionDeclaration* uninit_func )
93✔
422
  {
423
    auto defined_params = defined_func->parameters();
93✔
424
    auto uninit_params = uninit_func->parameters();
93✔
425
    auto is_defined_variadic = !defined_params.empty() && defined_params.back().get().rest;
93✔
426
    auto is_uninit_variadic = !uninit_params.empty() && uninit_params.back().get().rest;
93✔
427
    FunctionParameterDeclaration* bad_param = nullptr;
93✔
428
    std::string details;
93✔
429

430
    if ( is_defined_variadic != is_uninit_variadic ||
93✔
431
         defined_params.size() != uninit_params.size() || defined_func->type != uninit_func->type )
186✔
432
    {
433
      details =
434
          fmt::format( "Expecting {} with {}{} parameters, got {} with {}{} parameters.",
16✔
435
                       uninit_func->type, uninit_params.size(), is_uninit_variadic ? "+" : "",
16✔
436
                       defined_func->type, defined_params.size(), is_defined_variadic ? "+" : "" );
48✔
437
    }
438
    else
439
    {
440
      // Size has already been checked for equivalence but keeping both checks for clarity.
441
      for ( size_t i = 0; i < defined_params.size() && i < uninit_params.size(); ++i )
222✔
442
      {
443
        auto& defined_param = defined_params[i].get();
149✔
444
        auto& uninit_param = uninit_params[i].get();
149✔
445

446
        if ( defined_param.byref != uninit_param.byref )
149✔
447
        {
448
          details = fmt::format(
2✔
449
              "Parameter {} ('{}') is passed {} in uninitialized function but {} in defined "
450
              "function.",
451
              i + 1, defined_param.name.string(), uninit_param.byref ? "by reference" : "by value",
4✔
452
              defined_param.byref ? "by reference" : "by value" );
4✔
453
          bad_param = &defined_param;
2✔
454

455
          break;  // Stop on first error
2✔
456
        }
457
        if ( uninit_param.uninit_default && defined_param.default_value() == nullptr )
147✔
458
        {
459
          details = fmt::format( "Parameter {} ('{}') must have a default value.", i + 1,
4✔
460
                                 defined_param.name.string() );
6✔
461
          bad_param = &defined_param;
2✔
462

463
          break;  // Stop on first error
2✔
464
        }
465
      }
466
    }
467
    if ( !details.empty() )
93✔
468
    {
469
      report.error( bad_param ? bad_param->source_location : defined_func->source_location,
20✔
470
                    "Class method '{}' does not correctly implement uninitialized function '{}':\n"
471
                    "  {}\n"
472
                    "  See also: {}",
473
                    defined_func->scoped_name(), uninit_func->scoped_name(), details,
40✔
474
                    uninit_func->source_location );
20✔
475
    }
476
  };
93✔
477

478
  for ( const auto& uninit_fun_ref : all_uninit_functions )
450✔
479
  {
480
    auto& uninit_func = uninit_fun_ref.get();
107✔
481
    report.debug( uninit_func, "Class '{}' inherits uninitialized function '{}::{}'",
×
482
                  class_decl->name, uninit_func.scope, uninit_func.name );
107✔
483

484
    if ( uninit_func.type == UserFunctionType::Constructor )
107✔
485
    {
486
      report_error_if_not_same( class_decl->constructor_link->user_function(), &uninit_func );
16✔
487
    }
488
    else if ( auto method_itr = all_methods.find( uninit_func.name );
91✔
489
              method_itr != all_methods.end() )
91✔
490
    {
491
      report_error_if_not_same( method_itr->second, &uninit_func );
75✔
492
    }
493
    else if ( auto nonmethod_func = std::ranges::find_if(
32✔
494
                  workspace.user_functions,
16✔
495
                  [&]( const auto& uf )
22✔
496
                  {
497
                    return Clib::caseInsensitiveEqual( uf->name, uninit_func.name ) &&
24✔
498
                           uf->scope == class_decl->name;
24✔
499
                  } );
500
              nonmethod_func != workspace.user_functions.end() )
16✔
501
    {
502
      // This will _always_ error, reporting the defined function as static, and the uninit function
503
      // as method.
504
      report_error_if_not_same( nonmethod_func->get(), &uninit_func );
2✔
505
    }
506
    else
507
    {
508
      // A "not implemented" error will happen if the function is static and unreferenced (ie. does
509
      // not exist in workspace.user_functions but exists in
510
      // function_resolver.available_user_function_parse_trees).
511
      report.error( class_decl->source_location,
×
512
                    "Class '{}' does not implement uninitialized function '{}'\n"
513
                    "  See also: {}",
514
                    class_decl->name, uninit_func.scoped_name(), uninit_func.source_location );
14✔
515
    }
516
  }
517

518
  analyzed_classes.insert( class_decl );
343✔
519
}
343✔
520

521
class CaseDispatchDuplicateSelectorAnalyzer : public NodeVisitor
522
{
523
public:
524
  explicit CaseDispatchDuplicateSelectorAnalyzer( Report& report ) : report( report ) {}
110✔
525

526
  void visit_block( Block& ) override
348✔
527
  {
528
    // just don't recurse into children
529
  }
348✔
530

531
  void visit_integer_value( IntegerValue& node ) override
160✔
532
  {
533
    auto seen = already_seen_integers.find( node.value );
160✔
534
    if ( seen != already_seen_integers.end() )
160✔
535
    {
536
      report.error( node,
2✔
537
                    "case statement already has a selector for integer value {}.\n"
538
                    "  See also: {}",
539
                    node.value, ( *seen ).second->source_location );
2✔
540
    }
541
    else
542
    {
543
      already_seen_integers[node.value] = &node;
158✔
544
    }
545
  }
160✔
546

547
  void visit_string_value( StringValue& node ) override
102✔
548
  {
549
    auto seen = already_seen_strings.find( node.value );
102✔
550
    if ( seen != already_seen_strings.end() )
102✔
551
    {
552
      report.error( node,
2✔
553
                    "case statement already has a selector for string value {}.\n"
554
                    "  See also: {}",
555
                    Clib::getencodedquotedstring( node.value ), ( *seen ).second->source_location );
4✔
556
    }
557
    else
558
    {
559
      already_seen_strings[node.value] = &node;
100✔
560
    }
561
  }
102✔
562

563
  void visit_case_dispatch_default_selector( CaseDispatchDefaultSelector& node ) override
62✔
564
  {
565
    if ( already_seen_default )
62✔
566
    {
567
      report.error( node,
2✔
568
                    "case statement already has a default clause.\n"
569
                    "  See also: {}",
570
                    already_seen_default->source_location );
2✔
571
    }
572
    else
573
    {
574
      already_seen_default = &node;
60✔
575
    }
576
  }
62✔
577

578
private:
579
  Report& report;
580

581
  CaseDispatchDefaultSelector* already_seen_default = nullptr;
582
  std::map<int, IntegerValue*> already_seen_integers;
583
  std::map<std::string, StringValue*> already_seen_strings;
584
};
585

586

587
void SemanticAnalyzer::visit_case_statement( CaseStatement& case_ast )
110✔
588
{
589
  CaseDispatchDuplicateSelectorAnalyzer duplicate_detector( report );
110✔
590
  case_ast.dispatch_groups().accept( duplicate_detector );
110✔
591

592
  FlowControlScope break_scope( break_scopes, case_ast.source_location, case_ast.get_label(),
110✔
593
                                case_ast.break_label );
220✔
594

595
  visit_children( case_ast );
110✔
596
}
110✔
597

598
void SemanticAnalyzer::visit_case_dispatch_group( CaseDispatchGroup& dispatch_group )
348✔
599
{
600
  FlowControlScope break_scope( break_scopes, dispatch_group.source_location, "",
348✔
601
                                dispatch_group.break_label );
348✔
602

603
  visit_children( dispatch_group );
348✔
604
}
348✔
605

606
class CaseDispatchSelectorAnalyzer : public NodeVisitor
607
{
608
public:
609
  explicit CaseDispatchSelectorAnalyzer( Report& report ) : report( report ) {}
348✔
610

611
  void visit_identifier( Identifier& identifier ) override
×
612
  {
613
    report.error( identifier, "Case selector '{}' is not a constant.", identifier.name() );
×
614
  }
×
615

616
  void visit_string_value( StringValue& sv ) override
102✔
617
  {
618
    if ( sv.value.size() >= 254 )
102✔
619
    {
620
      report.error( sv, "String expressions in CASE statements must be <= 253 characters." );
×
621
    }
622
  }
102✔
623

624
private:
625
  Report& report;
626
};
627

628
void SemanticAnalyzer::visit_case_dispatch_selectors( CaseDispatchSelectors& selectors )
348✔
629
{
630
  visit_children( selectors );
348✔
631

632
  CaseDispatchSelectorAnalyzer selector_analyzer( report );
348✔
633
  selectors.accept( selector_analyzer );
348✔
634
}
348✔
635

636
void SemanticAnalyzer::visit_cstyle_for_loop( CstyleForLoop& loop )
69✔
637
{
638
  visit_loop_statement( loop );
69✔
639
}
69✔
640

641
void SemanticAnalyzer::visit_do_while_loop( DoWhileLoop& do_while )
11✔
642
{
643
  visit_loop_statement( do_while );
11✔
644
}
11✔
645

646
void SemanticAnalyzer::visit_foreach_loop( ForeachLoop& node )
478✔
647
{
648
  if ( report_function_name_conflict( node.source_location, node.iterator_name,
478✔
649
                                      "foreach iterator" ) )
650
  {
651
    return;
2✔
652
  }
653

654
  node.expression().accept( *this );
476✔
655

656
  LocalVariableScope scope( local_scopes, node.local_variable_scope_info );
476✔
657
  scope.create( node.iterator_name, WarnOn::Never, node.source_location );
476✔
658
  scope.create( "_" + node.iterator_name + "_expr", WarnOn::Never, node.source_location );
476✔
659
  scope.create( "_" + node.iterator_name + "_iter", WarnOn::Never, node.source_location );
476✔
660

661
  FlowControlScope break_scope( break_scopes, node.source_location, node.get_label(),
476✔
662
                                node.break_label );
952✔
663
  FlowControlScope continue_scope( continue_scopes, node.source_location, node.get_label(),
476✔
664
                                   node.continue_label );
952✔
665

666
  node.block().accept( *this );
476✔
667
}
476✔
668

669
ScopeName& SemanticAnalyzer::current_scope_name()
11,916✔
670
{
671
  return current_scope_names.top();
11,916✔
672
}
673

674
void SemanticAnalyzer::visit_function_call( FunctionCall& fc )
18,904✔
675
{
676
  bool is_super_call =
677
      // The linked function is a SuperFunction
678
      ( fc.function_link->user_function() &&
18,904✔
679
        fc.function_link->user_function()->type == UserFunctionType::Super ) ||
56,556✔
680

681
      ( !fc.function_link->function() &&  // no linked function
19,224✔
682
        fc.scoped_name &&                 // there is a name in the call (ie. not an expression)
476✔
683
        Clib::caseInsensitiveEqual( fc.scoped_name->string(),
408✔
684
                                    Compiler::SUPER ) );  // the name is "super"
18,904✔
685

686
  // No function linked through FunctionResolver
687
  if ( !fc.function_link->function() )
18,904✔
688
  {
689
    if ( is_super_call && !globals.find( Compiler::SUPER ) && !locals.find( Compiler::SUPER ) )
496✔
690
    {
691
      report.error( fc, "In call to 'super': No base class defines a constructor." );
2✔
692
      return;  // skip "Unknown identifier" error
2✔
693
    }
694

695
    // Method name may be set to variable name, eg: `var foo; foo();` If so,
696
    // clear it out and insert it at the children start to set as callee.
697
    if ( fc.scoped_name )
474✔
698
    {
699
      // If the a function is a class, then it did not define a constructor (since
700
      // there was no function linked).
701

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

705
      auto class_itr = workspace.all_class_locations.find( class_name );
202✔
706
      if ( class_itr == workspace.all_class_locations.end() )
202✔
707
      {
708
        class_name = fc.scoped_name->scope.string();
194✔
709

710
        class_itr = workspace.all_class_locations.find( class_name );
194✔
711
        if ( class_itr == workspace.all_class_locations.end() )
194✔
712
        {
713
          class_name.clear();
186✔
714
        }
715
      }
716

717
      if ( !class_name.empty() )
202✔
718
      {
719
        // There may be a variable named the same as the class, eg:
720
        //
721
        //   class Animal() var Animal; endclass
722
        //
723
        // If that is the case, there will be a global named
724
        // `class_name::class_name`. We will not error in this case.
725
        if ( Clib::caseInsensitiveEqual( fc.scoped_name->name, class_name ) &&
42✔
726
             !globals.find( ScopableName( class_name, class_name ).string() ) )
42✔
727
        {
728
          bool has_base_classes = false;
10✔
729

730
          if ( auto class_decl_itr = workspace.class_declaration_indexes.find( class_name );
10✔
731
               class_decl_itr != workspace.class_declaration_indexes.end() &&
20✔
732
               !workspace.class_declarations[class_decl_itr->second]->parameters().empty() )
20✔
733
          {
734
            has_base_classes = true;
4✔
735
          }
736

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

741
          auto func_itr = workspace.all_function_locations.find(
10✔
742
              ScopableName( class_name, class_name ).string() );
20✔
743

744
          if ( func_itr != workspace.all_function_locations.end() )
10✔
745
          {
746
            msg += fmt::format( "\n  See also (missing 'this' parameter?): {}", func_itr->second );
8✔
747
          }
748

749
          report.error( fc, msg );
10✔
750
          return;  // skip "Unknown identifier" error
10✔
751
        }
10✔
752
      }
753

754
      auto callee = std::make_unique<Identifier>( fc.source_location, *fc.scoped_name );
192✔
755
      fc.children.insert( fc.children.begin(), std::move( callee ) );
192✔
756
      fc.scoped_name.reset();
192✔
757
    }
202✔
758

759
    // For function calls where the callee is not an identifier, take the
760
    // arguments as-is. We don't support named args, as we don't know the
761
    // function to execute until runtime.
762
    auto any_named = std::find_if( fc.children.begin() + 1, fc.children.end(),
464✔
763
                                   []( const std::unique_ptr<Node>& node )
899✔
764
                                   {
765
                                     const auto& arg_name =
766
                                         static_cast<Argument*>( node.get() )->identifier;
899✔
767
                                     return arg_name != nullptr;
899✔
768
                                   } );
769

770
    if ( any_named != fc.children.end() )
464✔
771
    {
772
      report.error( fc, "In function call: Cannot use named arguments here." );
2✔
773

774
      return;
2✔
775
    }
776

777
    visit_children( fc );
462✔
778

779
    return;
462✔
780
  }
781

782
  // here we turn the arguments passed (which can be named or positional)
783
  // into the final_arguments vector, which is just one parameter per
784
  // argument, in the correct order.
785

786
  typedef std::map<std::string, std::unique_ptr<Expression>> ArgumentList;
787
  ArgumentList arguments_passed;
18,428✔
788

789
  typedef std::list<std::unique_ptr<Expression>> VariadicArguments;
790
  VariadicArguments variadic_arguments;
18,428✔
791

792
  bool any_named = false;
18,428✔
793
  auto uf = fc.function_link->user_function();
18,428✔
794

795
  std::vector<std::unique_ptr<Argument>> arguments = fc.take_arguments();
18,428✔
796
  auto parameters = fc.parameters();
18,428✔
797
  bool has_class_inst_parameter = false;
18,428✔
798

799
  bool in_super_func = false;
18,428✔
800
  bool in_constructor_func = false;
18,428✔
801
  bool in_generated_function = false;
18,428✔
802

803
  if ( !user_functions.empty() )
18,428✔
804
  {
805
    if ( user_functions.top()->type == UserFunctionType::Super )
7,594✔
806
    {
807
      in_super_func = true;
239✔
808
    }
809
    else if ( user_functions.top()->type == UserFunctionType::Constructor )
7,355✔
810
    {
811
      in_constructor_func = true;
386✔
812
      in_generated_function = dynamic_cast<GeneratedFunction*>( user_functions.top() );
386✔
813
    }
814
  }
815

816
  if ( uf )
18,428✔
817
  {
818
    // A super() call can only be used in a constructor function.
819
    if ( is_super_call && !in_constructor_func )
4,896✔
820
    {
821
      report.error( fc, "In call to '{}': super() can only be used in constructor functions.",
×
822
                    uf->name );
2✔
823
      return;
2✔
824
    }
825
    // Constructor functions are defined as `Constr( this )` and called
826
    // statically via `Constr()`. Provide a `this` parameter at this function
827
    // call site. Only do this when calling constructors outside of a
828
    // compiler-generated function (ie. super or generated constructor)
829
    if ( uf->type == UserFunctionType::Constructor && !in_generated_function && !in_super_func )
4,894✔
830
    {
831
      // A super call inherits the `this` argument
832
      if ( is_super_call )
308✔
833
      {
834
        // Super will use "this" argument
835
        arguments.insert( arguments.begin(),
×
836
                          std::make_unique<Argument>(
×
837
                              fc.source_location,
×
838
                              std::make_unique<Identifier>( fc.source_location, "this" ), false ) );
×
839

840
        report.debug( fc, "using ctor Identifier is_super_call={} in_super_func={} uf->name={}",
×
841
                      is_super_call, in_super_func, uf->name );
×
842
      }
843
      else
844
      {
845
        // Should never happen
846
        if ( uf->class_link == nullptr || uf->class_link->class_declaration() == nullptr )
308✔
847
        {
848
          uf->internal_error(
×
849
              fmt::format( "no class declaration found for user function '{}'", uf->name ) );
×
850
        }
851

852
        // Constructor will create a new "this" instance
853
        arguments.insert( arguments.begin(),
308✔
854
                          std::make_unique<Argument>(
308✔
855
                              fc.source_location,
308✔
856
                              std::make_unique<ClassInstance>(
308✔
857
                                  fc.source_location, uf->class_link->class_declaration() ),
308✔
858
                              false ) );
308✔
859

860
        report.debug( fc, "using ClassInstance is_super_call={} in_super_func={} uf->name={}",
×
861
                      is_super_call, in_super_func, uf->name );
308✔
862

863
        // Check class for uninit functions defined.
864
        analyze_class( uf->class_link->class_declaration() );
308✔
865
      }
866
      // Since a `this` argument is generated for constructor functions, disallow passing an
867
      // argument named `this`.
868
      has_class_inst_parameter = true;
308✔
869
    }
870
    else if ( uf->type == UserFunctionType::Super )
4,586✔
871
    {
872
      // Super will use "this" argument
873
      arguments.insert( arguments.begin(),
154✔
874
                        std::make_unique<Argument>(
154✔
875
                            fc.source_location,
154✔
876
                            std::make_unique<Identifier>( fc.source_location, "this" ), false ) );
308✔
877

878
      report.debug( fc, "using super Identifier is_super_call={} in_super_func={} uf->name={}",
154✔
879
                    is_super_call, in_super_func, uf->name );
154✔
880
    }
881
  }
882

883
  auto is_callee_variadic = !parameters.empty() && parameters.back().get().rest;
18,426✔
884

885
  const auto method_name = fc.string();
18,426✔
886

887
  for ( auto& arg_unique_ptr : arguments )
42,380✔
888
  {
889
    auto& arg = *arg_unique_ptr;
23,960✔
890
    std::string arg_name = arg.identifier ? arg.identifier->string() : "";
23,960✔
891

892
    if ( arg.spread )
23,960✔
893
    {
894
      if ( !uf )  // a module function
182✔
895
      {
896
        report.error( arg,
2✔
897
                      "In call to '{}': Spread operator cannot be used in module function call.",
898
                      method_name );
899
        return;
2✔
900
      }
901
      if ( !uf->is_variadic() )
180✔
902
      {
903
        report.error( arg,
2✔
904
                      "In call to '{}': Spread operator can only be used in variadic functions.",
905
                      method_name );
906
        return;
2✔
907
      }
908
      if ( arguments_passed.size() < parameters.size() - 1 )
178✔
909
      {
910
        report.error( arg,
2✔
911
                      "In call to '{}': Spread operator can only be used for arguments on or after "
912
                      "the formal rest parameter.",
913
                      method_name );
914
        return;
2✔
915
      }
916
    }
917

918
    if ( arg_name.empty() )
23,954✔
919
    {
920
      // Allow spread elements to come after named arguments, eg:
921
      //
922
      //  `foo( optA := 1, optB := 2, ... c )`
923
      //
924
      if ( any_named && !arg.spread )
22,996✔
925
      {
926
        report.error( arg, "In call to '{}': Unnamed args cannot follow named args.", method_name );
×
927
        return;
×
928
      }
929

930
      // Too many arguments passed?
931
      if ( arguments_passed.size() >= parameters.size() )
22,996✔
932
      {
933
        // Allowed if variadic
934
        if ( is_callee_variadic )
295✔
935
        {
936
          variadic_arguments.push_back( arg.take_expression() );
291✔
937

938
          // Do not add to `arguments_passed`, so continue.
939
          continue;
291✔
940
        }
941

942
        auto expected_args =
943
            static_cast<int>( parameters.size() ) - ( has_class_inst_parameter ? 1 : 0 );
4✔
944

NEW
945
        report.error( arg, "In call to '{}': Too many arguments passed.  Expected {}, got {}.",
×
946
                      method_name, expected_args, arguments.size() );
4✔
947
        continue;
4✔
948
      }
4✔
949

950
      arg_name = parameters.at( arguments_passed.size() ).get().name.string();
22,701✔
951
    }
952
    else
953
    {
954
      any_named = true;
958✔
955

956
      if ( has_class_inst_parameter && !in_super_func && !is_super_call &&
958✔
957
           Clib::caseInsensitiveEqual( arg_name, "this" ) )
958✔
958
      {
959
        report.error( arg, "In call to '{}': Cannot pass 'this' to constructor function.",
×
960
                      method_name );
961
        return;
×
962
      }
963
    }
964

965
    if ( arguments_passed.find( arg_name ) != arguments_passed.end() )
23,659✔
966
    {
967
      report.error( arg, "In call to '{}': Parameter '{}' passed more than once.", method_name,
×
968
                    arg_name );
969
      return;
×
970
    }
971

972
    // Inside a call to super(), if the arg is un-scoped, find the base class it belongs to. Error
973
    // if ambiguous.
974
    if ( is_super_call )
23,659✔
975
    {
976
      if ( arg.identifier && arg.identifier->global() && arg.identifier->string() != "this" )
338✔
977
      {
978
        std::string base_class;
17✔
979
        std::string first_location;
17✔
980
        std::string err_msg;
17✔
981

982
        auto add_location = [this]( std::string& where, const std::string& class_name )
57✔
983
        {
984
          fmt::format_to( std::back_inserter( where ), "  See: {}", class_name );
19✔
985

986
          auto funct_itr = workspace.all_function_locations.find(
19✔
987
              ScopableName( class_name, class_name ).string() );
38✔
988
          if ( funct_itr != workspace.all_function_locations.end() )
19✔
989
          {
990
            fmt::format_to( std::back_inserter( where ), " {}\n", funct_itr->second );
38✔
991
          }
992
          else
993
          {
994
            where += "\n";
×
995
          }
996
        };
19✔
997

998
        for ( auto& param_ref : parameters )
83✔
999
        {
1000
          auto& param = param_ref.get();
66✔
1001
          const auto& param_name = param.name.name;
66✔
1002
          if ( Clib::caseInsensitiveEqual( param_name, arg_name ) )
66✔
1003
          {
1004
            if ( !base_class.empty() )
19✔
1005
            {
1006
              if ( err_msg.empty() )
2✔
1007
              {
1008
                fmt::format_to( std::back_inserter( err_msg ),
4✔
1009
                                "In call to '{}': Ambiguous parameter '{}'.\n{}", method_name,
1010
                                param_name, first_location );
1011
              }
1012

1013
              add_location( err_msg, param.name.scope.string() );
2✔
1014
            }
1015
            else
1016
            {
1017
              base_class = param.name.scope.string();
17✔
1018

1019
              add_location( first_location, base_class );
17✔
1020
            }
1021
          }
1022
        }
1023
        if ( !err_msg.empty() )
17✔
1024
        {
1025
          report.error( fc, err_msg );
2✔
1026
        }
1027
        else
1028
        {
1029
          arg_name = ScopableName( base_class, arg_name ).string();
15✔
1030
        }
1031
      }
17✔
1032
    }
1033

1034

1035
    arguments_passed[arg_name] = arg.take_expression();
23,659✔
1036
  }
23,960✔
1037

1038
  std::vector<std::unique_ptr<Node>> final_arguments;
18,420✔
1039

1040
  for ( auto& param_ref : parameters )
53,136✔
1041
  {
1042
    FunctionParameterDeclaration& param = param_ref.get();
34,734✔
1043
    auto itr = arguments_passed.find( param.name.string() );
34,734✔
1044
    if ( itr == arguments_passed.end() )
34,734✔
1045
    {
1046
      if ( auto default_value = param.default_value() )
11,085✔
1047
      {
1048
        SimpleValueCloner cloner( report, default_value->source_location );
11,040✔
1049
        auto final_argument = cloner.clone( *default_value );
11,040✔
1050

1051
        if ( final_argument )
11,040✔
1052
        {
1053
          final_arguments.push_back( std::move( final_argument ) );
11,038✔
1054
        }
1055
        else
1056
        {
1057
          report.error(
×
1058
              param, "In call to '{}': Unable to create argument from default for parameter '{}'.",
1059
              method_name, param.name );
2✔
1060
          return;
2✔
1061
        }
1062
      }
11,042✔
1063
      else if ( !param.rest )
45✔
1064
      {
1065
        report.error( fc,
×
1066
                      "In call to '{}': Parameter '{}' was not passed, and there is no default.",
1067
                      method_name, param.name );
16✔
1068
        return;
16✔
1069
      }
1070
    }
1071
    else
1072
    {
1073
      final_arguments.push_back( std::move( ( *itr ).second ) );
23,649✔
1074
      arguments_passed.erase( itr );
23,649✔
1075
    }
1076
  }
1077

1078
  if ( is_callee_variadic )
18,402✔
1079
  {
1080
    // Push the leftover arguments into the call.
1081
    for ( auto& arg : variadic_arguments )
538✔
1082
    {
1083
      final_arguments.push_back( std::move( arg ) );
291✔
1084
    }
1085
  }
1086
  else
1087
  {
1088
    for ( auto& unused_argument : arguments_passed )
18,157✔
1089
    {
1090
      report.error( *unused_argument.second,
2✔
1091
                    "In call to '{}': Parameter '{}' passed by name, but the function has no "
1092
                    "such parameter.",
1093
                    method_name, unused_argument.first );
2✔
1094
    }
1095
    if ( !arguments_passed.empty() || arguments.size() > parameters.size() )
18,155✔
1096
      return;
6✔
1097
  }
1098

1099
  fc.children = std::move( final_arguments );
18,396✔
1100

1101
  // do this afterwards, so that named parameters will not be looked up as identifiers.
1102
  visit_children( fc );
18,396✔
1103
}
18,578✔
1104

1105
void SemanticAnalyzer::visit_function_parameter_list( FunctionParameterList& node )
3,521✔
1106
{
1107
  // A rest parameter can only be the last parameter in the list. Since we
1108
  // iterate in reverse, start at `true` and set to `false` after first
1109
  // iteration.
1110
  bool can_have_rest_parameter = true;
3,521✔
1111

1112
  for ( auto& child : std::views::reverse( node.children ) )
7,990✔
1113
  {
1114
    child->accept( *this );
4,469✔
1115

1116
    bool has_rest_parameter = static_cast<FunctionParameterDeclaration*>( child.get() )->rest;
4,469✔
1117

1118
    if ( has_rest_parameter && !can_have_rest_parameter )
4,469✔
1119
    {
1120
      report.error( *child, "Rest parameter must be the last parameter in the list." );
2✔
1121
    }
1122

1123
    can_have_rest_parameter = false;
4,469✔
1124
  }
1125
}
3,521✔
1126

1127
void SemanticAnalyzer::visit_function_parameter_declaration( FunctionParameterDeclaration& node )
4,469✔
1128
{
1129
  auto node_name = node.name.string();
4,469✔
1130
  if ( auto default_value = node.default_value() )
4,469✔
1131
  {
1132
    ConstantValidator validator;
335✔
1133
    // By accident, 0-parameter system function calls are allowed as constant values.
1134
    // They are not allowed as default parameters, though.
1135
    if ( !validator.validate( *default_value ) || dynamic_cast<FunctionCall*>( default_value ) )
335✔
1136
    {
1137
      report.error( node,
6✔
1138
                    "Parameter '{}' has a disallowed default.  Only simple operands are allowed as "
1139
                    "default arguments.",
1140
                    node_name );
1141
      // but continue, to avoid unknown identifier errors
1142
    }
1143
  }
335✔
1144
  if ( auto existing = locals.find( node_name ) )
8,938✔
1145
  {
1146
    report.error( node, "Parameter '{}' already defined.", node_name );
×
1147
    return;
×
1148
  }
4,469✔
1149

1150
  if ( node.rest && node.default_value() )
4,469✔
1151
  {
1152
    report.error( node, "Rest parameter '{}' cannot have a default value.", node_name );
2✔
1153
    return;
2✔
1154
  }
1155

1156
  WarnOn warn_on = node.unused ? WarnOn::IfUsed : WarnOn::IfNotUsed;
4,467✔
1157

1158
  if ( report_function_name_conflict( node.source_location, node_name, "function parameter" ) )
4,467✔
1159
  {
1160
    warn_on = WarnOn::Never;
2✔
1161
  }
1162

1163
  local_scopes.current_local_scope()->create( node_name, warn_on, node.source_location );
4,467✔
1164
}
4,469✔
1165

1166
void SemanticAnalyzer::visit_function_expression( FunctionExpression& node )
675✔
1167
{
1168
  if ( auto user_function = node.function_link->user_function() )
675✔
1169
  {
1170
    // Create a new capture scope for this function. It must be in a new C++
1171
    // scope for to add the captures to
1172
    // `user_function->capture_variable_scope_info` via the user function
1173
    // visitor.
1174
    {
1175
      FunctionVariableScope new_capture_scope( captures );
675✔
1176
      LocalVariableScope capture_scope( capture_scopes,
675✔
1177
                                        user_function->capture_variable_scope_info );
675✔
1178
      FunctionVariableScope new_function_scope( locals );
675✔
1179
      visit_user_function( *user_function );
675✔
1180
    }
675✔
1181

1182
    // Since the capture_scope was popped (above), any _existing_ capture scope refers to
1183
    // the parent function expression in the tree. Adjust that function to inherit this function's
1184
    // captures.
1185
    if ( auto cap_scope = capture_scopes.current_local_scope() )
675✔
1186
    {
1187
      for ( auto& variable : user_function->capture_variable_scope_info.variables )
417✔
1188
      {
1189
        // If the capture is not local, we must capture it
1190
        if ( !locals.find( variable->name ) )
604✔
1191
        {
1192
          // If already captured, set the variables capture to the existing.
1193
          if ( auto captured = captures.find( variable->name ) )
354✔
1194
          {
1195
            variable->capturing = captured;
96✔
1196
          }
1197
          // Otherwise, create new.
1198
          else if ( !captures.find( variable->name ) )
162✔
1199
          {
1200
            // Create a new capture variable in the parent function, setting
1201
            // this function expression's captured variable to this newly
1202
            // created one.
1203
            variable->capturing = cap_scope->capture( variable->capturing );
81✔
1204
          }
177✔
1205
        }
1206
      }
1207
    }
1208
  }
1209
}
675✔
1210

1211
void SemanticAnalyzer::visit_function_reference( FunctionReference& node )
765✔
1212
{
1213
  if ( auto function = node.function_link->user_function() )
765✔
1214
  {
1215
    if ( function->type == UserFunctionType::Super )
761✔
1216
    {
1217
      report.error( node, "Cannot reference super() function." );
2✔
1218
    }
1219
    else if ( function->type == UserFunctionType::Constructor )
759✔
1220
    {
1221
      if ( auto class_decl = function->class_link->class_declaration() )
160✔
1222
        analyze_class( class_decl );
160✔
1223
      else
1224
      {
1225
        // Should never happen, since a constructor function would always have a
1226
        // link to its class.
1227
        function->internal_error( fmt::format(
×
1228
            "no class declaration found for class constructor '{}'", function->name ) );
×
1229
      }
1230
    }
1231
  }
1232
  else
1233
  {
1234
    report.error( node, "User function '{}' not found", node.name );
4✔
1235
  }
1236
}
765✔
1237

1238
void SemanticAnalyzer::visit_identifier( Identifier& node )
31,632✔
1239
{
1240
  // Resolution order:
1241
  //
1242
  // if scoped: locals -> globals
1243
  // otherwise: local function -> local captures -> ancestor (above) functions -> globals
1244
  //
1245
  const auto& name = node.scoped_name.string();
31,632✔
1246

1247
  // If there is a scope, whether it is (":foo") empty or not ("Animal:foo"),
1248
  // we need to check both globals and locals.
1249
  if ( !node.scoped_name.scope.empty() )
31,632✔
1250
  {
1251
    if ( !node.scoped_name.scope.global() )
282✔
1252
    {
1253
      if ( auto local = locals.find( name ) )
524✔
1254
      {
1255
        local->mark_used();
191✔
1256
        node.variable = local;
191✔
1257
      }
262✔
1258
    }
1259
    // Did not find it in locals, check globals
1260
    if ( !node.variable )
564✔
1261
    {
1262
      if ( auto scoped_global = globals.find( name ) )
182✔
1263
      {
1264
        node.variable = scoped_global;
83✔
1265
      }
91✔
1266
    }
1267
  }
1268
  else
1269
  {
1270
    if ( auto local = locals.find( name ) )
62,700✔
1271
    {
1272
      local->mark_used();
21,488✔
1273
      node.variable = local;
21,488✔
1274
    }
1275
    else if ( auto captured = captures.find( name ) )
19,724✔
1276
    {
1277
      // Should already be marked used as it's not newly created (done below).
1278
      // There is no `captures.find_in_ancestors()` check because if an upper
1279
      // capture was found, we still need to capture it for our own function (done
1280
      // below).
1281
      node.variable = captured;
213✔
1282
    }
1283
    else if ( auto ancestor = locals.find_in_ancestors( name ) )
19,298✔
1284
    {
1285
      // Capture the variable. In a deeply nested capture, this will reference the
1286
      // local in the ancestor function. The function expression visitor will swap
1287
      // the 'capturing' to a local-safe variable.
1288
      node.variable = capture_scopes.current_local_scope()->capture( ancestor );
339✔
1289
      node.variable->mark_used();
339✔
1290
    }
1291
    else if ( auto global = globals.find( name ) )
18,620✔
1292
    {
1293
      node.variable = global;
9,270✔
1294
    }
1295
    else if ( !current_scope_name().global() )
40✔
1296
    {
1297
      const auto scoped_name = ScopableName( current_scope_name(), node.name() ).string();
12✔
1298

1299
      // We do not support nested classes, so if there is a `current_scope`, it would only _ever_
1300
      // exist in globals.
1301
      if ( auto scoped_global = globals.find( scoped_name ) )
24✔
1302
      {
1303
        node.variable = scoped_global;
12✔
1304
      }
12✔
1305
    }
60,183✔
1306
  }
1307

1308
  if ( !node.variable )
63,264✔
1309
  {
1310
    report.error( node, "Unknown identifier '{}'.", name );
36✔
1311
    return;
36✔
1312
  }
1313
}
31,632✔
1314

1315
void SemanticAnalyzer::visit_variable_binding( VariableBinding& node )
768✔
1316
{
1317
  if ( auto variable = create_variable( node.source_location, node.scoped_name.scope.string(),
768✔
1318
                                        node.scoped_name.name ) )
1,536✔
1319
  {
1320
    node.variable = std::move( variable );
768✔
1321
    visit_children( node );
768✔
1322
  }
768✔
1323
}
768✔
1324

1325
void SemanticAnalyzer::visit_jump_statement( JumpStatement& node )
647✔
1326
{
1327
  auto& scopes = node.jump_type == JumpStatement::Break ? break_scopes : continue_scopes;
647✔
1328

1329
  if ( auto scope = scopes.find( node.label ) )
647✔
1330
  {
1331
    node.flow_control_label = scope->flow_control_label;
647✔
1332
    node.local_variables_to_remove = locals.count() - scope->local_variables_size;
647✔
1333
  }
1334
  else
1335
  {
1336
    auto type_str = node.jump_type == JumpStatement::Break ? "break" : "continue";
×
1337

1338
    if ( !node.label.empty() && break_scopes.any() )
×
1339
      report.error( node, "Label '{}' not found for {}", node.label, type_str );
×
1340
    else
1341
      report.error( node, "Cannot {} here.", type_str );
×
1342
  }
1343
}
647✔
1344

1345

1346
void SemanticAnalyzer::visit_loop_statement( LoopStatement& loop )
567✔
1347
{
1348
  FlowControlScope continue_scope( continue_scopes, loop.source_location, loop.get_label(),
567✔
1349
                                   loop.continue_label );
1,134✔
1350
  FlowControlScope break_scope( break_scopes, loop.source_location, loop.get_label(),
567✔
1351
                                loop.break_label );
1,134✔
1352

1353
  visit_children( loop );
567✔
1354
}
567✔
1355

1356
void SemanticAnalyzer::visit_program( Program& program )
452✔
1357
{
1358
  LocalVariableScope scope( local_scopes, program.local_variable_scope_info );
452✔
1359

1360
  visit_children( program );
452✔
1361
}
452✔
1362

1363
void SemanticAnalyzer::visit_program_parameter_declaration( ProgramParameterDeclaration& node )
208✔
1364
{
1365
  if ( auto existing = locals.find( node.name ) )
416✔
1366
  {
1367
    report.error( node, "Parameter '{}' already defined.", node.name );
×
1368
    return;
×
1369
  }
208✔
1370
  WarnOn warn_on = node.unused ? WarnOn::IfUsed : WarnOn::IfNotUsed;
208✔
1371

1372
  if ( report_function_name_conflict( node.source_location, node.name, "program parameter" ) )
208✔
1373
  {
1374
    warn_on = WarnOn::Never;
2✔
1375
  }
1376

1377
  local_scopes.current_local_scope()->create( node.name, warn_on, node.source_location );
208✔
1378
}
1379

1380
void SemanticAnalyzer::visit_repeat_until_loop( RepeatUntilLoop& node )
38✔
1381
{
1382
  visit_loop_statement( node );
38✔
1383
}
38✔
1384

1385
void SemanticAnalyzer::visit_return_statement( ReturnStatement& node )
4,789✔
1386
{
1387
  if ( !user_functions.empty() )
4,789✔
1388
  {
1389
    auto uf = user_functions.top();
4,483✔
1390

1391
    if ( uf->type == UserFunctionType::Constructor && !node.children.empty() )
4,483✔
1392
    {
1393
      report.error( node, "Cannot return a value from a constructor function." );
2✔
1394
    }
1395
  }
1396

1397
  visit_children( node );
4,789✔
1398
}
4,789✔
1399

1400
void SemanticAnalyzer::visit_sequence_binding( SequenceBinding& node )
111✔
1401
{
1402
  u8 index = 0;
111✔
1403
  VariableBinding* previous_rest_binding = nullptr;
111✔
1404

1405
  if ( node.binding_count() > 127 )
111✔
1406
  {
1407
    report.error( node, "Too many binding elements. Maximum is 127." );
×
1408
  }
1409

1410
  for ( const auto& child : node.children )
445✔
1411
  {
1412
    if ( auto index_binding = dynamic_cast<VariableBinding*>( child.get() ) )
334✔
1413
    {
1414
      if ( index_binding->rest )
319✔
1415
      {
1416
        if ( previous_rest_binding != nullptr )
73✔
1417
        {
1418
          report.error( *index_binding,
2✔
1419
                        "Only one rest binding is allowed.\n"
1420
                        "  See also: {}",
1421
                        previous_rest_binding->source_location );
2✔
1422
        }
1423

1424
        previous_rest_binding = index_binding;
73✔
1425
        node.rest_index = index;
73✔
1426
      }
1427
    }
1428

1429
    ++index;
334✔
1430
  }
1431

1432
  visit_children( node );
111✔
1433
}
111✔
1434

1435
void SemanticAnalyzer::visit_user_function( UserFunction& node )
3,521✔
1436
{
1437
  // Track current scope for use in visit_identifier
1438
  current_scope_names.emplace( node.scope );
3,521✔
1439
  user_functions.emplace( &node );
3,521✔
1440
  if ( node.exported )
3,521✔
1441
  {
1442
    if ( !node.scope.empty() )
571✔
1443
    {
1444
      report.error( node, "Exported function '{}' cannot be scoped.", node.scoped_name() );
2✔
1445
    }
1446
    else
1447
    {
1448
      unsigned max_name_length = sizeof( Pol::Bscript::BSCRIPT_EXPORTED_FUNCTION::funcname ) - 1;
569✔
1449
      if ( node.name.length() > max_name_length )
569✔
1450
      {
1451
        report.error( node,
×
1452
                      "Exported function name '{}' is too long at {} characters.  Max length: {}",
1453
                      node.name, node.name.length(), max_name_length );
2✔
1454
      }
1455
    }
1456
  }
1457

1458
  LocalVariableScope scope( local_scopes, node.local_variable_scope_info );
3,521✔
1459
  visit_children( node );
3,521✔
1460
  user_functions.pop();
3,521✔
1461
  current_scope_names.pop();
3,521✔
1462
}
3,521✔
1463

1464
void SemanticAnalyzer::visit_var_statement( VarStatement& node )
5,877✔
1465
{
1466
  if ( auto variable = create_variable( node.source_location, node.scope, node.name ) )
11,754✔
1467
  {
1468
    node.variable = std::move( variable );
5,873✔
1469
    visit_children( node );
5,873✔
1470
  }
5,877✔
1471
}
5,877✔
1472

1473
void SemanticAnalyzer::visit_variable_assignment_statement( VariableAssignmentStatement& node )
2,102✔
1474
{
1475
  visit_children( node );
2,102✔
1476

1477
  if ( auto bop = dynamic_cast<BinaryOperator*>( &node.rhs() ) )
2,102✔
1478
  {
1479
    if ( bop->token_id == TOK_ASSIGN )
204✔
1480
    {
1481
      if ( auto second_ident = dynamic_cast<Identifier*>( &bop->lhs() ) )
3✔
1482
      {
1483
        if ( node.identifier().variable == second_ident->variable )
×
1484
        {
1485
          // we have something like
1486
          //      a := a := expr;
1487
          report.warning( node, "Double-assignment to the same variable '{}'.",
×
1488
                          node.identifier().name() );
×
1489
        }
1490
      }
1491
    }
1492
  }
1493
}
2,102✔
1494

1495
void SemanticAnalyzer::visit_while_loop( WhileLoop& node )
221✔
1496
{
1497
  visit_loop_statement( node );
221✔
1498
}
221✔
1499

1500
void SemanticAnalyzer::visit_constant_loop( ConstantPredicateLoop& node )
228✔
1501
{
1502
  visit_loop_statement( node );
228✔
1503
}
228✔
1504

1505
std::shared_ptr<Variable> SemanticAnalyzer::create_variable( const SourceLocation& source_location,
6,645✔
1506
                                                             const std::string& scope,
1507
                                                             const std::string& name )
1508
{
1509
  auto maybe_scoped_name = ScopableName( scope, name ).string();
6,645✔
1510

1511
  // Since this is not scoped check, we cannot have `Animal::FOO` and a constant `FOO`.
1512
  if ( auto constant = workspace.constants.find( name ) )
6,645✔
1513
  {
1514
    report.error( source_location,
×
1515
                  "Cannot define a variable with the same name as constant '{}'.\n"
1516
                  "  See also: {}",
1517
                  name, constant->source_location );
4✔
1518
    return {};
4✔
1519
  }
1520

1521
  report_function_name_conflict( source_location, maybe_scoped_name, "variable" );
6,641✔
1522

1523
  if ( auto local_scope = local_scopes.current_local_scope() )
6,641✔
1524
  {
1525
    return local_scope->create( maybe_scoped_name, WarnOn::Never, source_location );
3,976✔
1526
  }
1527

1528
  if ( auto existing = globals.find( maybe_scoped_name ) )
5,330✔
1529
  {
NEW
1530
    report.error( source_location,
×
1531
                  "Global variable '{}' already defined.\n"
1532
                  "  See also: {}",
NEW
1533
                  maybe_scoped_name, existing->source_location );
×
NEW
1534
    return {};
×
1535
  }
2,665✔
1536

1537
  return globals.create( maybe_scoped_name, 0, WarnOn::Never, source_location );
2,665✔
1538
}
6,645✔
1539

1540
bool SemanticAnalyzer::report_function_name_conflict( const SourceLocation& referencing_loc,
11,864✔
1541
                                                      const std::string& function_name,
1542
                                                      const std::string& element_description )
1543
{
1544
  return report_function_name_conflict(
23,728✔
1545
      workspace, report, referencing_loc,
11,864✔
1546
      ScopableName( current_scope_name(), function_name ).string(), element_description );
23,728✔
1547
}
1548

1549
bool SemanticAnalyzer::report_function_name_conflict( const CompilerWorkspace& workspace,
180,745✔
1550
                                                      Report& report,
1551
                                                      const SourceLocation& referencing_loc,
1552
                                                      const std::string& function_name,
1553
                                                      const std::string& element_description )
1554
{
1555
  auto func_itr = workspace.all_function_locations.find( function_name );
180,745✔
1556
  if ( func_itr != workspace.all_function_locations.end() )
180,745✔
1557
  {
1558
    const SourceLocation& function_loc = ( *func_itr ).second;
16✔
1559
    report.error( referencing_loc,
16✔
1560
                  "Cannot define a {} with the same name as function '{}'.\n"
1561
                  "  Defined here: {}",
1562
                  element_description, function_name, function_loc );
1563
    return true;
16✔
1564
  }
1565
  return false;
180,729✔
1566
}
1567

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