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

polserver / polserver / 17472095514

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

push

github

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

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

* add more ctor inheritance tests

* skip "Unknown identifier" error for calling undefined ctor

* add test for actual, previously crashing source

* add test for actual, previously crashing source

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

* add super local var test

* make register_available_generated_function private

* remove ClassDeclaration::has_super_ctor

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

* add core-changes

* use constant for "super" string

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

3 existing lines in 2 files now uncovered.

43721 of 73078 relevant lines covered (59.83%)

433591.61 hits per line

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

97.37
/pol-core/bscript/compiler/astbuilder/GeneratedFunctionBuilder.cpp
1
#include "GeneratedFunctionBuilder.h"
2

3
#include <list>
4
#include <ranges>
5
#include <set>
6

7
#include "bscript/compiler/Report.h"
8
#include "bscript/compiler/ast/Argument.h"
9
#include "bscript/compiler/ast/ArrayInitializer.h"
10
#include "bscript/compiler/ast/ClassDeclaration.h"
11
#include "bscript/compiler/ast/FunctionBody.h"
12
#include "bscript/compiler/ast/FunctionCall.h"
13
#include "bscript/compiler/ast/FunctionParameterDeclaration.h"
14
#include "bscript/compiler/ast/FunctionParameterList.h"
15
#include "bscript/compiler/ast/FunctionReference.h"
16
#include "bscript/compiler/ast/GeneratedFunction.h"
17
#include "bscript/compiler/ast/Identifier.h"
18
#include "bscript/compiler/ast/ReturnStatement.h"
19
#include "bscript/compiler/ast/SpreadElement.h"
20
#include "bscript/compiler/ast/UserFunction.h"
21
#include "bscript/compiler/ast/ValueConsumer.h"
22
#include "bscript/compiler/astbuilder/BuilderWorkspace.h"
23
#include "bscript/compiler/astbuilder/SimpleValueCloner.h"
24
#include "bscript/compiler/model/ClassLink.h"
25
#include "bscript/compiler/model/FunctionLink.h"
26
#include "bscript/compiler/model/ScopableName.h"
27

28
namespace Pol::Bscript::Compiler
29
{
30
GeneratedFunctionBuilder::GeneratedFunctionBuilder( const SourceFileIdentifier& loc,
173✔
31
                                                    BuilderWorkspace& workspace )
173✔
32
    : CompoundStatementBuilder( loc, workspace )
173✔
33
{
34
}
173✔
35

36
void GeneratedFunctionBuilder::super_function( std::unique_ptr<GeneratedFunction>& super )
151✔
37
{
38
  std::vector<UserFunction*> base_class_ctors;
151✔
39
  std::set<ClassDeclaration*> visited;
151✔
40
  std::list<std::shared_ptr<ClassLink>> to_visit;
151✔
41
  auto class_declaration = super->class_declaration();
151✔
42

43
  to_visit.insert( to_visit.end(), class_declaration->base_class_links.begin(),
151✔
44
                   class_declaration->base_class_links.end() );
45

46
  for ( auto to_link_itr = to_visit.begin(); to_link_itr != to_visit.end();
397✔
47
        to_link_itr = to_visit.erase( to_link_itr ) )
246✔
48
  {
49
    if ( auto base_cd = ( *to_link_itr )->class_declaration() )
246✔
50
    {
51
      if ( visited.find( base_cd ) != visited.end() )
246✔
52
      {
53
        continue;
×
54
      }
55
      visited.insert( base_cd );
246✔
56

57
      if ( auto constructor_link = base_cd->constructor_link )
492✔
58
      {
59
        if ( auto base_class_ctor = constructor_link->user_function() )
243✔
60
        {
61
          base_class_ctors.push_back( base_class_ctor );
243✔
62
        }
63
      }
246✔
64
    }
65
  }
66

67
  build( super, base_class_ctors );
151✔
68
}
151✔
69

70
void GeneratedFunctionBuilder::constructor_function(
22✔
71
    std::unique_ptr<GeneratedFunction>& constructor )
72
{
73
  std::set<ClassDeclaration*> visited;
22✔
74
  std::list<std::shared_ptr<ClassLink>> to_visit;
22✔
75
  auto class_declaration = constructor->class_declaration();
22✔
76
  UserFunction* base_class_ctor = nullptr;
22✔
77

78
  to_visit.insert( to_visit.end(), class_declaration->base_class_links.begin(),
22✔
79
                   class_declaration->base_class_links.end() );
80

81
  for ( auto to_link_itr = to_visit.begin(); to_link_itr != to_visit.end();
22✔
UNCOV
82
        to_link_itr = to_visit.erase( to_link_itr ) )
×
83
  {
84
    if ( auto base_cd = ( *to_link_itr )->class_declaration() )
22✔
85
    {
86
      if ( visited.find( base_cd ) != visited.end() )
22✔
87
      {
88
        continue;
×
89
      }
90
      visited.insert( base_cd );
22✔
91

92
      if ( auto constructor_link = base_cd->constructor_link )
44✔
93
      {
94
        if ( auto ctor = constructor_link->user_function() )
22✔
95
        {
96
          base_class_ctor = ctor;
22✔
97
          break;
22✔
98
        }
99
      }
22✔
100
    }
101
  }
102

103
  if ( base_class_ctor != nullptr )
22✔
104
  {
105
    build( constructor, { base_class_ctor } );
22✔
106
  }
107

108
  // Set the generated function as the class' constructor function.
109
  class_declaration->constructor_link =
110
      std::make_unique<FunctionLink>( constructor->source_location, "", true );
22✔
111
  class_declaration->constructor_link->link_to( constructor.get() );
22✔
112
}
22✔
113

114
void GeneratedFunctionBuilder::build( std::unique_ptr<GeneratedFunction>& function,
173✔
115
                                      std::vector<UserFunction*> base_class_ctors )
116
{
117
  const auto& loc = function->source_location;
173✔
118
  // If there are no base ctors, skip updating the function's parameters and
119
  // body, therefore a super function that is "invalid" will have no parameters
120
  // or body.
121
  if ( !base_class_ctors.empty() )
173✔
122
  {
123
    auto& function_parameters = function->child<FunctionParameterList>( 0 ).children;
173✔
124

125
    function_parameters.push_back( std::make_unique<FunctionParameterDeclaration>(
173✔
126
        loc, ScopableName( ScopeName::None, "this" ), true /* byref */, false /* unused */,
346✔
127
        false /* rest */ ) );
173✔
128

129
    // Our super() alias'ed parameter can be a rest parameter only if the not-last
130
    // constructors are not variadic.
131
    bool can_use_rest = true;
173✔
132

133
    for ( const auto* base_class_ctor :
173✔
134
          base_class_ctors | std::views::take( base_class_ctors.size() - 1 ) )
412✔
135
    {
136
      if ( base_class_ctor->is_variadic() )
80✔
137
      {
138
        can_use_rest = false;
14✔
139
        break;
14✔
140
      }
141
    }
142

143
    std::set<ScopableName> visited_arg_names;
173✔
144

145
    for ( auto base_class_ctor : base_class_ctors )
438✔
146
    {
147
      add_base_constructor( function, base_class_ctor, visited_arg_names, can_use_rest );
265✔
148
    }
149
  }
173✔
150

151
  std::string desc;
173✔
152
  Node::describe_tree_to_indented( *function, desc, 0 );
173✔
153
  workspace.report.debug( loc, "Generated function: {}", desc );
173✔
154
}
173✔
155

156
void GeneratedFunctionBuilder::add_base_constructor( std::unique_ptr<GeneratedFunction>& super,
265✔
157
                                                     UserFunction* base_class_ctor,
158
                                                     std::set<ScopableName>& visited_arg_names,
159
                                                     bool can_use_rest )
160
{
161
  auto& function_parameters = super->child<FunctionParameterList>( 0 ).children;
265✔
162
  auto& body = super->child<FunctionBody>( 1 ).children;
265✔
163
  auto params = base_class_ctor->parameters();
265✔
164
  const auto& loc = super->source_location;
265✔
165
  auto call_arguments = std::vector<std::unique_ptr<Argument>>();
265✔
166

167
  bool first = true;
265✔
168

169
  for ( auto& param_ref : params )
721✔
170
  {
171
    auto& param = param_ref.get();
458✔
172
    auto param_scope = first                      ? ScopeName::None
458✔
173
                       : param.name.scope.empty() ? ScopeName( base_class_ctor->name )
193✔
174
                                                  : param.name.scope;
458✔
175

176
    auto param_name = ScopableName( param_scope, param.name.name );
458✔
177

178

179
    // Skip the first `this` parameter of the function declaration, as we've already added it.
180
    if ( !first && visited_arg_names.find( param_name ) == visited_arg_names.end() )
458✔
181
    {
182
      // If the base ctor parameter is a rest param, our super() function parameter
183
      // will _not_ be a rest, but a regular variable with a default [empty]
184
      // array value. This only applies if we _cannot_ use rest parameters.
185
      if ( param.rest && !can_use_rest )
190✔
186
      {
187
        function_parameters.push_back( std::make_unique<FunctionParameterDeclaration>(
38✔
188
            loc, param_name, param.byref, param.unused, false,
38✔
189
            std::make_unique<ArrayInitializer>( param.source_location,
76✔
190
                                                std::vector<std::unique_ptr<Expression>>() ) ) );
38✔
191
      }
192
      // If the base ctor parameter has a default value, our super() function
193
      // parameter will have the same default value.
194
      else if ( auto default_value = param.default_value() )
152✔
195
      {
196
        SimpleValueCloner cloner( report, default_value->source_location );
11✔
197

198
        // Value must be cloneable
199
        if ( auto final_argument = cloner.clone( *default_value ) )
11✔
200
        {
201
          function_parameters.push_back( std::make_unique<FunctionParameterDeclaration>(
9✔
202
              loc, param_name, param.byref, false /* unused */, false /* rest */,
18✔
203
              std::move( final_argument ) ) );
9✔
204
        }
205
        else
206
        {
207
          report.error( loc,
2✔
208
                        "In construction of '{}': Unable to create argument from default for "
209
                        "parameter '{}'.\n"
210
                        "  See also: {}",
211
                        super->scoped_name(), param.name, param.source_location );
4✔
212
          return;
2✔
213
        }
11✔
214
      }
11✔
215
      else
216
      {
217
        // This super() alias'ed parameter is a rest parameter if the base
218
        // ctor's parameter is rest _and_ we can use a rest parameter.
219
        auto is_rest_param = param.rest && can_use_rest;
141✔
220

221
        function_parameters.push_back( std::make_unique<FunctionParameterDeclaration>(
141✔
222
            loc, param_name, param.byref, false /* unused */, is_rest_param ) );
282✔
223
      }
224
    }
225

226
    // By default, the function call argument inside this super()'s function
227
    // declaration will just be the super() alias'ed parameter.
228
    std::unique_ptr<Expression> call_argument =
229
        std::make_unique<Identifier>( param.source_location, param_name );
456✔
230

231
    // If the base ctor parameter is a rest param, the base_ctor() function
232
    // call will spread the super() function parameter -- an array -- into
233
    // the base constructor.
234
    if ( param.rest )
456✔
235
    {
236
      // Passing an element that is not spreadable (eg. a string) will result
237
      // in an empty array passed to the base constructor.
238
      call_argument = std::make_unique<SpreadElement>( param.source_location,
88✔
239
                                                       std::move( call_argument ), false );
132✔
240
    }
241

242
    call_arguments.insert( call_arguments.end(),
456✔
243
                           std::make_unique<Argument>( param.source_location, param.name,
912✔
244
                                                       std::move( call_argument ), param.rest ) );
456✔
245
    visited_arg_names.insert( param_name );
456✔
246
    first = false;
456✔
247
  }
460✔
248

249
  auto fc = std::make_unique<FunctionCall>(
250
      loc, base_class_ctor->name, ScopableName( base_class_ctor->name, base_class_ctor->name ),
526✔
251
      std::move( call_arguments ) );
526✔
252

253
  fc->function_link->link_to( base_class_ctor );
263✔
254

255
  auto value_consumer = std::make_unique<ValueConsumer>( fc->source_location, std::move( fc ) );
263✔
256
  body.push_back( std::move( value_consumer ) );
263✔
257
}
267✔
258
}  // 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