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

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

93.06
/pol-core/bscript/compiler/astbuilder/FunctionResolver.cpp
1
#include "FunctionResolver.h"
2

3
#include <list>
4

5
#include "bscript/compiler/Profile.h"
6
#include "bscript/compiler/Report.h"
7
#include "bscript/compiler/ast/ClassDeclaration.h"
8
#include "bscript/compiler/ast/Function.h"
9
#include "bscript/compiler/ast/ModuleFunctionDeclaration.h"
10
#include "bscript/compiler/ast/UserFunction.h"
11
#include "bscript/compiler/astbuilder/AvailableParseTree.h"
12
#include "bscript/compiler/file/SourceFileIdentifier.h"
13
#include "bscript/compiler/file/SourceLocation.h"
14
#include "bscript/compiler/model/ClassLink.h"
15
#include "bscript/compiler/model/FunctionLink.h"
16
#include "clib/strutil.h"
17

18
namespace Pol::Bscript::Compiler
19
{
20
FunctionResolver::FunctionResolver( Report& report ) : report( report ) {}
2,354✔
21
FunctionResolver::~FunctionResolver() = default;
2,354✔
22

23
void FunctionResolver::force_reference( const ScopableName& name, const SourceLocation& loc )
513✔
24
{
25
  register_function_link( name, std::make_shared<FunctionLink>( loc, "" /* calling scope */ ) );
513✔
26
}
513✔
27

28
void FunctionResolver::force_reference( const ScopeName& scope_name, const SourceLocation& loc )
118✔
29
{
30
  register_class_link( scope_name, std::make_shared<ClassLink>( loc, scope_name.string() ) );
118✔
31
}
118✔
32

33
void FunctionResolver::register_available_generated_function( const SourceLocation& loc,
191✔
34
                                                              const ScopableName& name,
35
                                                              Node* context, UserFunctionType type )
36
{
37
  auto agf = std::make_unique<AvailableGeneratedFunction>( loc, context, name, type );
191✔
38
  register_available_function_parse_tree( loc, name, std::move( agf ) );
191✔
39
}
191✔
40

41
void FunctionResolver::register_available_user_function(
2,461✔
42
    const SourceLocation& source_location,
43
    EscriptGrammar::EscriptParser::FunctionDeclarationContext* ctx, bool force_reference )
44
{
45
  ScopableName name( ScopeName::Global, ctx->IDENTIFIER()->getText() );
2,461✔
46

47
  // Exported functions are always forced to be referenced so the code generator
48
  // will emit the function's instructions, even though there is no FunctionCall
49
  // to it.
50
  register_available_user_function_parse_tree( source_location, ctx, std::move( name ),
2,461✔
51
                                               force_reference || ctx->EXPORTED() );
2,461✔
52
}
2,461✔
53

54

55
void FunctionResolver::register_available_scoped_function(
681✔
56
    const SourceLocation& source_location, const ScopeName& class_name,
57
    EscriptGrammar::EscriptParser::FunctionDeclarationContext* ctx )
58
{
59
  ScopableName name( class_name, ctx->IDENTIFIER()->getText() );
681✔
60
  register_available_user_function_parse_tree( source_location, ctx, std::move( name ),
681✔
61
                                               ctx->EXPORTED() );
681✔
62
}
681✔
63

64
void FunctionResolver::register_available_class_declaration(
636✔
65
    const SourceLocation& loc, const ScopeName& class_name,
66
    EscriptGrammar::EscriptParser::ClassDeclarationContext* ctx,
67
    Node* top_level_statements_child_node )
68
{
69
  register_available_class_decl_parse_tree( loc, ctx, class_name, top_level_statements_child_node );
636✔
70
}
636✔
71

72
void FunctionResolver::register_class_link( const ScopeName& name,
1,105✔
73
                                            std::shared_ptr<ClassLink> class_link )
74
{
75
  if ( resolve_if_existing( name, class_link ) )
1,105✔
76
    return;
783✔
77

78
  unresolved_classes[name].push_back( std::move( class_link ) );
322✔
79
}
80

81
void FunctionResolver::register_function_link( const ScopableName& name,
20,564✔
82
                                               std::shared_ptr<FunctionLink> function_link )
83
{
84
  const auto& calling_scope = function_link->calling_scope;
20,564✔
85
  const auto& unscoped_name = name.name;
20,564✔
86

87
  // If a call scope was given, _only_ check that one.
88
  if ( !name.scope.global() )
20,564✔
89
  {
90
    if ( resolve_if_existing( name, function_link ) )
1,143✔
91
      return;
64✔
92
  }
93
  else
94
  {
95
    // If calling scope present, check, eg. inside Animal class, `foo()` checks
96
    // `Animal::foo()`.
97
    if ( !calling_scope.empty() &&
20,121✔
98
         resolve_if_existing( { calling_scope, unscoped_name }, function_link ) )
20,121✔
99
      return;
×
100

101
    // Check global scope, only if calling scope is empty. If we are inside eg.
102
    // `class Animal` and call `foo()`, we do not want to link to an
103
    // _already-registered_ global function `foo()`. Adding this unresolved
104
    // function link with a empty scope name will eventually resolve it to the
105
    // correctly scoped function.
106
    if ( calling_scope.empty() &&
38,142✔
107
         resolve_if_existing( ScopableName( ScopeName::Global, unscoped_name ), function_link ) )
38,142✔
108
      return;
13,017✔
109
  }
110

111
  report.debug( function_link->source_location, "registering funct link {}", name.string() );
7,483✔
112

113
  unresolved_function_links[name].push_back( std::move( function_link ) );
7,483✔
114
}
115

116
std::string FunctionResolver::register_function_expression(
513✔
117
    const SourceLocation& source_location,
118
    EscriptGrammar::EscriptParser::FunctionExpressionContext* ctx )
119
{
120
  auto name = function_expression_name( source_location );
513✔
121
  auto apt =
122
      std::make_unique<AvailableParseTree>( source_location, ctx, ScopeName::Global, nullptr );
513✔
123
  available_user_function_parse_trees[name] = std::move( apt );
513✔
124
  return name;
1,026✔
125
}
513✔
126

127
void FunctionResolver::register_module_function( ModuleFunctionDeclaration* mf )
122,409✔
128
{
129
  const auto& name = mf->name;
122,409✔
130

131
  auto itr = available_user_function_parse_trees.find( name );
122,409✔
132
  if ( itr != available_user_function_parse_trees.end() )
122,409✔
133
  {
134
    const auto& previous = ( *itr ).second;
2✔
135

136
    report.error( previous->source_location,
2✔
137
                  "User Function '{}' conflicts with Module Function of the same name.\n"
138
                  "  Module Function declaration: {}",
139
                  name, mf->source_location );
2✔
140
  }
141

142
  resolved_functions[{ ScopeName::Global, name }] = mf;
122,409✔
143
  resolved_functions[{ mf->scope, name }] = mf;
122,409✔
144
}
122,409✔
145

146
void FunctionResolver::register_user_function( const std::string& scope, UserFunction* uf )
3,113✔
147
{
148
  ScopableName scoped_name( scope, uf->name );
3,113✔
149
  auto itr = resolved_functions.find( scoped_name );
3,113✔
150
  if ( itr != resolved_functions.end() )
3,113✔
151
  {
152
    // Throw an exception, this should _never_ happen. If this does, then an
153
    // AvailableParseTree was visited twice.
154
    auto msg = fmt::format( "duplicate user function definition for {}: {} vs {}", uf->name,
×
155
                            uf->source_location, ( *itr ).second->source_location );
×
156
    uf->internal_error( msg );
×
157
  }
×
158

159
  resolved_functions[scoped_name] = uf;
3,113✔
160
  report.debug( uf->source_location, "registering uf {}", scoped_name );
3,113✔
161

162
  // Constructors are registered in global scope
163
  if ( uf->type == UserFunctionType::Constructor )
3,113✔
164
  {
165
    ScopableName global_name( ScopeName::Global, uf->name );
396✔
166

167
    resolved_functions[global_name] = uf;
396✔
168
    report.debug( uf->source_location, "registering uf {}", global_name );
396✔
169
  }
396✔
170
}
3,113✔
171

172
void FunctionResolver::register_class_declaration( ClassDeclaration* cd )
458✔
173
{
174
  resolved_classes[cd->name] = cd;
458✔
175

176
  // Since we are _registering_ the class declaration, that means either the
177
  // user has actively instantiated the class via `Foo()` or a base class has
178
  // requested `Foo`. Register all the class' methods and include them at
179
  // instruction generation. See also: `Optimizer::optimize` where Methods and
180
  // Constructors are force-referenced.
181
  //
182
  // The `methods` object only contains methods, as the constructor is in the
183
  // `constructor_link`.
184
  for ( const auto& [method_name, function_link] : cd->methods )
664✔
185
  {
186
    register_function_link( { cd->name, method_name }, function_link );
206✔
187
  }
188
}
458✔
189

190
bool FunctionResolver::resolve(
3,764✔
191
    std::vector<std::unique_ptr<AvailableSecondPassTarget>>& to_build_ast )
192
{
193
  // Attempt to link all unresolved function links
194
  for ( auto unresolved_itr = unresolved_function_links.begin();
3,764✔
195
        unresolved_itr != unresolved_function_links.end(); )
11,211✔
196
  {
197
    for ( auto function_link_itr = ( *unresolved_itr ).second.begin();
7,447✔
198
          function_link_itr != ( *unresolved_itr ).second.end(); )
22,642✔
199
    {
200
      auto& function_link = *function_link_itr;
15,195✔
201
      const auto& calling_scope = function_link->calling_scope;
15,195✔
202
      const auto& name = ( *unresolved_itr ).first;
15,195✔
203
      const auto& unscoped_name = name.name;
15,195✔
204
      const auto& call_scope = name.scope;
15,195✔
205
      bool is_super_scoped = call_scope.super();
15,195✔
206

207
      // Link the function if possible, otherwise try to build it.
208
      auto link_handled = [&]( const ScopableName& key )
16,067✔
209
      {
210
        // A super:: call cannot be handled by the current call scope.
211
        if ( call_scope.super() && Clib::caseInsensitiveEqual( key.scope.string(), calling_scope ) )
16,067✔
212
        {
213
          return false;
73✔
214
        }
215

216
        if ( resolve_if_existing( key, function_link ) )
15,994✔
217
        {
218
          // Remove this function link from the list of links.
219
          function_link_itr = ( *unresolved_itr ).second.erase( function_link_itr );
7,235✔
220
          return true;
7,235✔
221
        };
222

223
        if ( build_if_available( to_build_ast, calling_scope, key ) )
8,759✔
224
        {
225
          // Keep the link in the list, as it may be resolved later.
226
          ++function_link_itr;
3,569✔
227
          return true;
3,569✔
228
        }
229
        return false;
5,190✔
230
      };
15,195✔
231

232
      auto handled_by_scopes = [&]( const std::string& starting_scope )
6,619✔
233
      {
234
        std::set<std::string> visited;
6,619✔
235
        std::list<std::string> to_check( { starting_scope } );
19,857✔
236
        bool handled = false;
6,619✔
237

238
        for ( auto to_check_itr = to_check.begin(); !handled && to_check_itr != to_check.end();
8,672✔
239
              to_check_itr = to_check.erase( to_check_itr ) )
2,053✔
240
        {
241
          if ( visited.find( *to_check_itr ) != visited.end() )
6,901✔
242
            continue;
659✔
243

244
          visited.insert( *to_check_itr );
6,881✔
245

246
          if ( link_handled( { *to_check_itr, unscoped_name } ) )
6,881✔
247
          {
248
            return true;
4,848✔
249
          }
250

251
          auto cd_itr = resolved_classes.find( *to_check_itr );
2,071✔
252
          if ( cd_itr == resolved_classes.end() )
2,071✔
253
            continue;
639✔
254

255
          auto cd = cd_itr->second;
1,432✔
256

257
          for ( const auto& base_cd_link : cd->base_class_links )
2,310✔
258
          {
259
            if ( auto base_cd = base_cd_link->class_declaration() )
916✔
260
            {
261
              to_check.push_back( base_cd->name );
330✔
262
            }
263
            // If using a call to super:: and this base class has not been
264
            // loaded, we can't resolve the link just yet: we wait until the
265
            // base class list (at least, for this current `cd`) is loaded.
266
            else if ( is_super_scoped )
586✔
267
            {
268
              return false;
38✔
269
            }
270
          }
271
        }
272

273
        return false;
1,771✔
274
      };
13,238✔
275

276
      report.debug( function_link->source_location, "resolving funct link {}", name );
15,195✔
277

278
      // If a call scope was given (except super::), we check that call scope
279
      // and its ancestors _without_ checking Global (ie. skipping the check
280
      // done in the `else` block)
281
      if ( !call_scope.empty() && !call_scope.super() )
15,195✔
282
      {
283
        if ( handled_by_scopes( call_scope.string() ) )
5,388✔
284
          continue;
10,804✔
285
      }
286
      else
287
      {
288
        if ( !calling_scope.empty() && handled_by_scopes( calling_scope ) )
9,807✔
289
          continue;
581✔
290

291
        // Check global scope (if not explicitly calling super:: scope)
292
        if ( !call_scope.super() && link_handled( { ScopeName::Global, unscoped_name } ) )
9,226✔
293
          continue;
5,994✔
294
      }
295

296
      // Link was not handled, move to the next one
297
      ++function_link_itr;
4,391✔
298
    }
299

300
    // If all links were resolved, remove the entry.
301
    if ( ( *unresolved_itr ).second.empty() )
7,447✔
302
    {
303
      unresolved_itr = unresolved_function_links.erase( unresolved_itr );
3,438✔
304
    }
305
    else
306
    {
307
      // Do not complain here, as the identifier in `[scope::]name` may be a variable.
308
      // Error handled in SemanticAnalyzer.
309
      ++unresolved_itr;
4,009✔
310
    }
311
  }
312

313
  for ( auto unresolved_itr = unresolved_classes.begin();
3,764✔
314
        unresolved_itr != unresolved_classes.end(); )
4,152✔
315
  {
316
    auto scope = unresolved_itr->first;
388✔
317

318
    for ( auto class_link_itr = ( *unresolved_itr ).second.begin();
388✔
319
          class_link_itr != ( *unresolved_itr ).second.end(); )
960✔
320
    {
321
      auto& class_link = *class_link_itr;
572✔
322

323
      report.debug( class_link->source_location, "resolving class link {}", scope.string() );
572✔
324

325
      // Link the function if possible, otherwise try to build it.
326
      if ( resolve_if_existing( scope, class_link ) )
572✔
327
      {
328
        // Remove this function link from the list of links.
329
        class_link_itr = ( *unresolved_itr ).second.erase( class_link_itr );
316✔
330
      }
331
      else if ( build_if_available( to_build_ast, scope ) )
256✔
332
      {
333
        // Keep the link in the list, as it may be resolved later.
334
        class_link_itr = ( *unresolved_itr ).second.erase( class_link_itr );
4✔
335
      }
336
      else
337
      {
338
        ++class_link_itr;
252✔
339
      }
340
    }
341

342
    // If all links were resolved, remove the entry.
343
    if ( ( *unresolved_itr ).second.empty() )
388✔
344
    {
345
      unresolved_itr = unresolved_classes.erase( unresolved_itr );
197✔
346
    }
347
    else
348
    {
349
      ++unresolved_itr;
191✔
350
    }
351
  }
388✔
352

353
  return !to_build_ast.empty();
3,764✔
354
}
355

356
std::string FunctionResolver::function_expression_name( const SourceLocation& source_location )
1,026✔
357
{
358
  return fmt::format( "funcexpr@{}:{}:{}", source_location.source_file_identifier->index,
1,026✔
359
                      source_location.range.start.line_number,
1,026✔
360
                      source_location.range.start.character_column );
2,052✔
361
}
362

363
void FunctionResolver::register_available_function_parse_tree(
3,333✔
364
    const SourceLocation& source_location, const ScopableName& name,
365
    std::unique_ptr<AvailableSecondPassTarget> apt )
366
{
367
  const auto& unscoped_name = name.name;
3,333✔
368

369
  // Eg. "foo" or "Animal::foo"
370
  auto scoped_name = name.string();
3,333✔
371

372
  // Eg. "", "Animal"
373
  auto scope = name.scope.string();
3,333✔
374

375
  report.debug( source_location, "registering funct apt {}", scoped_name );
3,333✔
376

377
  auto itr = available_user_function_parse_trees.find( scoped_name );
3,333✔
378
  if ( itr != available_user_function_parse_trees.end() )
3,333✔
379
  {
380
    auto& previous = ( *itr ).second;
2✔
381

382
    report.error( source_location,
2✔
383
                  "Function '{}' defined more than once.\n"
384
                  "  Previous declaration: {}",
385
                  scoped_name, previous->source_location );
2✔
386
  }
387

388
  auto itr2 = resolved_functions.find( { scope, unscoped_name } );
3,333✔
389
  if ( itr2 != resolved_functions.end() )
3,333✔
390
  {
391
    auto* previous = ( *itr2 ).second;
2✔
392

393
    std::string what = dynamic_cast<ModuleFunctionDeclaration*>( previous ) == nullptr
2✔
394
                           ? "User Function"
395
                           : "Module Function";
4✔
396

UNCOV
397
    report.error( source_location,
×
398
                  "User Function '{}' conflicts with {} of the same name.\n"
399
                  "  {} declaration: {}",
400
                  scoped_name, what, what, previous->source_location );
2✔
401
  }
2✔
402

403
  auto itr3 = available_class_decl_parse_trees.find( scope );
3,333✔
404
  if ( itr3 != available_class_decl_parse_trees.end() )
3,333✔
405
  {
406
    auto& previous = ( *itr3 ).second;
×
407

408
    report.error( source_location,
×
409
                  "User Function '{}' conflicts with Class of the same name.\n"
410
                  "  Class declaration: {}",
411
                  scoped_name, previous->source_location );
×
412
  }
413

414
  available_user_function_parse_trees[scoped_name] = std::move( apt );
3,333✔
415
}
3,333✔
416

417
void FunctionResolver::register_available_user_function_parse_tree(
3,142✔
418
    const SourceLocation& source_location, antlr4::ParserRuleContext* ctx, const ScopableName& name,
419
    bool force_reference )
420
{
421
  register_available_function_parse_tree(
3,142✔
422
      source_location, name,
423
      std::make_unique<AvailableParseTree>( source_location, ctx, name.scope, nullptr ) );
6,284✔
424

425
  if ( force_reference )
3,142✔
426
  {
427
    // just make sure there is an entry, so that we build an AST for it
428
    register_function_link( name, std::make_shared<FunctionLink>(
570✔
429
                                      source_location, name.scope.string() /* calling scope */ ) );
1,140✔
430
  }
431
}
3,142✔
432

433
void FunctionResolver::register_available_class_decl_parse_tree(
636✔
434
    const SourceLocation& source_location, antlr4::ParserRuleContext* ctx,
435
    const ScopeName& scope_name, Node* top_level_statements_child_node )
436
{
437
  const auto name = scope_name.string();
636✔
438
  report.debug( source_location, "registering class apt ({}).", name );
636✔
439
  auto itr = available_user_function_parse_trees.find( name );
636✔
440
  if ( itr != available_user_function_parse_trees.end() )
636✔
441
  {
442
    auto& previous = ( *itr ).second;
2✔
443

444
    report.error( source_location,
2✔
445
                  "Class '{}' conflicts with User Function of the same name.\n"
446
                  "  Previous declaration: {}",
447
                  name, previous->source_location );
2✔
448
  }
449

450
  auto itr2 = available_class_decl_parse_trees.find( name );
636✔
451
  if ( itr2 != available_class_decl_parse_trees.end() )
636✔
452
  {
453
    auto& previous = ( *itr2 ).second;
2✔
454
    report.error( source_location,
2✔
455
                  "Class '{}' defined more than once.\n"
456
                  "  Previous declaration: {}",
457
                  name, previous->source_location );
2✔
458
  }
459

460
  auto itr3 = resolved_functions.find( { ScopeName::Global, name } );
636✔
461
  if ( itr3 != resolved_functions.end() )
636✔
462
  {
463
    auto* previous = ( *itr3 ).second;
×
464

465
    std::string what = dynamic_cast<ModuleFunctionDeclaration*>( previous ) == nullptr
×
466
                           ? "User Function"
467
                           : "Module Function";
×
468

469
    report.error( source_location,
×
470
                  "Class '{}' conflicts with {} of the same name.\n"
471
                  "  {} declaration: {}",
472
                  name, what, what, previous->source_location );
×
473
  }
×
474

475
  auto apt = std::make_unique<AvailableParseTree>( source_location, ctx, scope_name,
476
                                                   top_level_statements_child_node );
636✔
477
  available_class_decl_parse_trees[name] = std::move( apt );
636✔
478
}
636✔
479

480
Function* FunctionResolver::check_existing( const ScopableName& key,
36,558✔
481
                                            bool requires_constructor ) const
482
{
483
  auto itr = resolved_functions.find( key );
36,558✔
484
  if ( itr != resolved_functions.end() )
36,558✔
485
  {
486
    if ( requires_constructor )
20,318✔
487
    {
488
      auto uf = dynamic_cast<UserFunction*>( ( *itr ).second );
675✔
489
      if ( !uf || uf->type != UserFunctionType::Constructor )
675✔
490
      {
491
        return nullptr;
2✔
492
      }
493
    }
494

495
    return ( *itr ).second;
20,316✔
496
  }
497
  return nullptr;
16,240✔
498
}
499

500
ClassDeclaration* FunctionResolver::check_existing( const ScopeName& key ) const
1,677✔
501
{
502
  auto itr = resolved_classes.find( key );
1,677✔
503
  if ( itr != resolved_classes.end() )
1,677✔
504
  {
505
    return ( *itr ).second;
1,099✔
506
  }
507
  return nullptr;
578✔
508
}
509

510
bool FunctionResolver::build_if_available(
8,759✔
511
    std::vector<std::unique_ptr<AvailableSecondPassTarget>>& to_build_ast,
512
    const std::string& calling_scope, const ScopableName& call )
513
{
514
  AvailableParseTreeMap::iterator itr;
8,759✔
515

516
  // If a call scope was given, _only_ check that one (except if super:: provided)
517
  // eg. `Animal::foo()` will only search for `Animal::foo()`, disregarding a possible parent
518
  // scoped `::foo()`.
519
  if ( !call.global() && !call.scope.super() )
8,759✔
520
  {
521
    itr = available_user_function_parse_trees.find( call.string() );
2,328✔
522
    if ( itr != available_user_function_parse_trees.end() )
2,328✔
523
    {
524
      to_build_ast.push_back( std::move( ( *itr ).second ) );
624✔
525
      available_user_function_parse_trees.erase( itr );
624✔
526
      report.debug( to_build_ast.back()->source_location, "adding to build funct [call] {}: {}",
624✔
527
                    *to_build_ast.back(), call.string() );
1,248✔
528
      return true;
624✔
529
    }
530

531
    // Check if there is a class available with that scope, as it could provide this function
532
    // later.
533
    if ( build_if_available( to_build_ast, call.scope ) )
1,704✔
534
    {
535
      return true;
230✔
536
    }
537

538
    // Nothing found. Can't build a scoped function call.
539
    return false;
1,474✔
540
  }
541

542
  // Inside a scope, eg. `Animal`...
543
  if ( !calling_scope.empty() )
6,431✔
544
  {
545
    // Check if exists in given `scope`, eg. `foo()` checks `Animal::foo()`
546
    auto handled_by_scope = [&]( const std::string& scope )
246✔
547
    {
548
      // Skip checking current scope if doing `super::` call.
549
      if ( call.scope.super() && Clib::caseInsensitiveEqual( scope, calling_scope ) )
246✔
550
      {
551
        return false;
×
552
      }
553
      auto scoped_call_name = fmt::format( "{}::{}", scope, call.name );
246✔
554
      itr = available_user_function_parse_trees.find( scoped_call_name );
246✔
555
      if ( itr != available_user_function_parse_trees.end() )
246✔
556
      {
557
        to_build_ast.push_back( std::move( ( *itr ).second ) );
×
558
        available_user_function_parse_trees.erase( itr );
×
559
        report.debug( to_build_ast.back()->source_location, "adding to build funct [scoped] {}: {}",
×
560
                      *to_build_ast.back(), scoped_call_name );
×
561
        return true;
×
562
      }
563
      return false;
246✔
564
    };
246✔
565

566
    std::set<std::string> visited;
199✔
567
    std::list<std::string> to_check( { calling_scope } );
597✔
568

569
    for ( auto to_check_itr = to_check.begin(); to_check_itr != to_check.end();
455✔
570
          to_check_itr = to_check.erase( to_check_itr ) )
256✔
571
    {
572
      auto cd_itr = resolved_classes.find( *to_check_itr );
256✔
573
      if ( cd_itr == resolved_classes.end() )
256✔
574
        continue;
10✔
575

576
      auto cd = cd_itr->second;
256✔
577
      if ( visited.find( cd->name ) != visited.end() )
256✔
578
        continue;
10✔
579

580
      visited.insert( cd->name );
246✔
581

582
      if ( handled_by_scope( cd->name ) )
246✔
583
      {
584
        break;
×
585
      }
586

587
      for ( const auto& base_cd_link : cd->base_class_links )
434✔
588
      {
589
        if ( auto base_cd = base_cd_link->class_declaration() )
188✔
590
        {
591
          to_check.push_back( base_cd->name );
57✔
592
        }
593
      }
594
    }
595
  }
199✔
596

597
  // Check if there is a class available with the function name.
598
  if ( build_if_available( to_build_ast, call.name ) )
6,431✔
599
  {
600
    return true;
226✔
601
  }
602

603
  // If call scope is empty, check if function is a constructor, eg. Foo() -> Foo::Foo
604
  // The register_user_function will only register the function in global scope if it _is_ a
605
  // constructor.
606
  if ( call.scope.global() )
6,205✔
607
  {
608
    auto scoped_call_name = fmt::format( "{}::{}", call.name, call.name );
6,205✔
609
    itr = available_user_function_parse_trees.find( scoped_call_name );
6,205✔
610
    if ( itr != available_user_function_parse_trees.end() )
6,205✔
611
    {
612
      to_build_ast.push_back( std::move( ( *itr ).second ) );
222✔
613
      available_user_function_parse_trees.erase( itr );
222✔
614
      report.debug( to_build_ast.back()->source_location, "adding to build funct [ctor?] {}: {}",
222✔
615
                    *to_build_ast.back(), scoped_call_name );
222✔
616
      return true;
222✔
617
    }
618
  }
6,205✔
619

620
  // Check if exists in global scope.
621
  itr = available_user_function_parse_trees.find( call.name );
5,983✔
622
  if ( itr != available_user_function_parse_trees.end() )
5,983✔
623
  {
624
    to_build_ast.push_back( std::move( ( *itr ).second ) );
2,267✔
625
    available_user_function_parse_trees.erase( itr );
2,267✔
626
    report.debug( to_build_ast.back()->source_location, "adding to build funct [global] {}: {}",
2,267✔
627
                  *to_build_ast.back(), call.name );
2,267✔
628
    return true;
2,267✔
629
  }
630

631
  return false;
3,716✔
632
}
199✔
633

634
bool FunctionResolver::build_if_available(
8,391✔
635
    std::vector<std::unique_ptr<AvailableSecondPassTarget>>& to_build_ast, const ScopeName& scope )
636
{
637
  auto itr = available_class_decl_parse_trees.find( scope.string() );
8,391✔
638
  if ( itr != available_class_decl_parse_trees.end() )
8,391✔
639
  {
640
    to_build_ast.push_back( std::move( ( *itr ).second ) );
460✔
641
    available_class_decl_parse_trees.erase( itr );
460✔
642
    report.debug( to_build_ast.back()->source_location, "adding to build class [call.scope] {}: {}",
460✔
643
                  *to_build_ast.back(), scope.string() );
920✔
644
    return true;
460✔
645
  }
646

647
  return false;
7,931✔
648
}
649

650
bool FunctionResolver::resolve_if_existing( const ScopableName& key,
36,558✔
651
                                            std::shared_ptr<FunctionLink>& function_link )
652
{
653
  auto resolved_function = check_existing( key, function_link->require_ctor );
36,558✔
654

655
  if ( resolved_function )
36,558✔
656
  {
657
    function_link->link_to( resolved_function );
20,316✔
658
    report.debug( function_link->source_location, "linking {} to {}::{} @ {}", key,
20,316✔
659
                  resolved_function->scope, resolved_function->name, (void*)resolved_function );
20,316✔
660
    return true;
20,316✔
661
  }
662

663
  return false;
16,242✔
664
}
665

666
bool FunctionResolver::resolve_if_existing( const ScopeName& scope,
1,677✔
667
                                            std::shared_ptr<ClassLink>& class_link )
668
{
669
  auto resolved_class = check_existing( scope );
1,677✔
670

671
  if ( resolved_class )
1,677✔
672
  {
673
    class_link->link_to( resolved_class );
1,099✔
674
    report.debug( class_link->source_location, "linking class {} to {} @ {}", scope.string(),
1,099✔
675
                  resolved_class->name, (void*)resolved_class );
1,099✔
676
    return true;
1,099✔
677
  }
678

679
  return false;
578✔
680
};
681
}  // 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