GCC Code Coverage Report


Directory: ../
File: src/symboltablebuilder/Scope.cpp
Date: 2025-02-05 01:09:36
Exec Total Coverage
Lines: 188 198 94.9%
Functions: 22 22 100.0%
Branches: 259 401 64.6%

Line Branch Exec Source
1 // Copyright (c) 2021-2025 ChilliBits. All rights reserved.
2
3 #include "Scope.h"
4
5 #include <SourceFile.h>
6 #include <ast/ASTNodes.h>
7 #include <exception/SemanticError.h>
8 #include <symboltablebuilder/SymbolTableBuilder.h>
9
10 namespace spice::compiler {
11
12 /**
13 * Create a child scope and return it
14 *
15 * @param scopeName Name of the child scope
16 * @param scopeType Type of the child scope
17 * @param declCodeLoc Code location of the scope
18 * @return Child scope (heap allocated)
19 */
20 18630 Scope *Scope::createChildScope(const std::string &scopeName, ScopeType scopeType, const CodeLoc *declCodeLoc) {
21
3/6
✓ Branch 0 (2→3) taken 18630 times.
✗ Branch 1 (2→14) not taken.
✓ Branch 2 (3→4) taken 18630 times.
✗ Branch 3 (3→12) not taken.
✓ Branch 4 (4→5) taken 18630 times.
✗ Branch 5 (4→10) not taken.
18630 children.insert({scopeName, std::make_shared<Scope>(this, sourceFile, scopeType, declCodeLoc)});
22 18630 return children.at(scopeName).get();
23 }
24
25 /**
26 * Rename the child scope. This is useful for realizing function overloading by storing a function with not
27 * only its name, but also its signature
28 *
29 * @param oldName Old name of the child table
30 * @param newName New name of the child table
31 */
32 9791 void Scope::renameChildScope(const std::string &oldName, const std::string &newName) {
33
4/8
✓ Branch 0 (2→3) taken 9791 times.
✗ Branch 1 (2→19) not taken.
✓ Branch 2 (3→4) taken 9791 times.
✗ Branch 3 (3→7) not taken.
✓ Branch 4 (4→5) taken 9791 times.
✗ Branch 5 (4→19) not taken.
✓ Branch 6 (5→6) taken 9791 times.
✗ Branch 7 (5→7) not taken.
9791 assert(children.contains(oldName) && !children.contains(newName));
34
1/2
✓ Branch 0 (8→9) taken 9791 times.
✗ Branch 1 (8→19) not taken.
9791 auto nodeHandler = children.extract(oldName);
35
1/2
✓ Branch 0 (10→11) taken 9791 times.
✗ Branch 1 (10→17) not taken.
9791 nodeHandler.key() = newName;
36
1/2
✓ Branch 0 (12→13) taken 9791 times.
✗ Branch 1 (12→16) not taken.
9791 children.insert(std::move(nodeHandler));
37 9791 }
38
39 /**
40 * Duplicates the child scope by copying it. The duplicated symbols point to the original ones.
41 *
42 * @param oldName Old name of the child block
43 * @param newName New block name
44 */
45 2897 Scope *Scope::copyChildScope(const std::string &oldName, const std::string &newName) {
46
4/8
✓ Branch 0 (2→3) taken 2897 times.
✗ Branch 1 (2→23) not taken.
✓ Branch 2 (3→4) taken 2897 times.
✗ Branch 3 (3→7) not taken.
✓ Branch 4 (4→5) taken 2897 times.
✗ Branch 5 (4→23) not taken.
✓ Branch 6 (5→6) taken 2897 times.
✗ Branch 7 (5→7) not taken.
2897 assert(children.contains(oldName) && !children.contains(newName));
47 // Create copy
48
2/4
✓ Branch 0 (8→9) taken 2897 times.
✗ Branch 1 (8→23) not taken.
✓ Branch 2 (10→11) taken 2897 times.
✗ Branch 3 (10→23) not taken.
2897 const std::shared_ptr<Scope> newScope = children.at(oldName)->deepCopyScope();
49 // Save copy under new name
50
2/4
✓ Branch 0 (11→12) taken 2897 times.
✗ Branch 1 (11→20) not taken.
✓ Branch 2 (12→13) taken 2897 times.
✗ Branch 3 (12→18) not taken.
2897 children.insert({newName, newScope});
51 5794 return newScope.get();
52 2897 }
53
54 /**
55 * Deep copy the current scope and all its children
56 *
57 * @return Deep copy of the current scope
58 */
59 10303 std::shared_ptr<Scope> Scope::deepCopyScope() { // NOLINT(misc-no-recursion)
60 10303 const std::shared_ptr<Scope> newScope = std::make_shared<Scope>(*this);
61
2/2
✓ Branch 0 (30→5) taken 7406 times.
✓ Branch 1 (30→31) taken 10303 times.
17709 for (const auto &[childName, oldChild] : children) {
62
2/4
✓ Branch 0 (9→10) taken 7406 times.
✗ Branch 1 (9→37) not taken.
✓ Branch 2 (11→12) taken 7406 times.
✗ Branch 3 (11→35) not taken.
7406 newScope->children[childName] = oldChild->deepCopyScope();
63
1/2
✓ Branch 0 (16→17) taken 7406 times.
✗ Branch 1 (16→38) not taken.
7406 newScope->children[childName]->parent = newScope.get();
64
2/4
✓ Branch 0 (19→20) taken 7406 times.
✗ Branch 1 (19→38) not taken.
✓ Branch 2 (22→23) taken 7406 times.
✗ Branch 3 (22→38) not taken.
7406 newScope->children[childName]->symbolTable.scope = newScope->children[childName].get();
65
1/2
✓ Branch 0 (26→27) taken 7406 times.
✗ Branch 1 (26→38) not taken.
7406 newScope->children[childName]->symbolTable.parent = &newScope->symbolTable;
66 }
67 10303 newScope->symbolTable.scope = newScope.get();
68 10303 return newScope;
69 }
70
71 /**
72 * Get a child scope of the current scope by its name
73 *
74 * @param scopeName Child scope name
75 * @return Child scope
76 */
77 39211 Scope *Scope::getChildScope(const std::string &scopeName) const {
78
5/6
✓ Branch 0 (3→4) taken 39210 times.
✓ Branch 1 (3→7) taken 1 times.
✓ Branch 2 (5→6) taken 39210 times.
✗ Branch 3 (5→7) not taken.
✓ Branch 4 (8→9) taken 39210 times.
✓ Branch 5 (8→11) taken 1 times.
39211 if (!children.empty() && children.contains(scopeName))
79 39210 return children.at(scopeName).get();
80 1 return nullptr;
81 }
82
83 /**
84 * Retrieve all variables in the current scope, that have reached the end of their lifetime at the end of this scope
85 *
86 * @return Collection of EOL variables
87 */
88 16706 std::vector<SymbolTableEntry *> Scope::getVarsGoingOutOfScope() { // NOLINT(misc-no-recursion)
89
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 16706 times.
16706 assert(parent != nullptr); // Should not be called in root scope
90 16706 std::vector<SymbolTableEntry *> varsGoingOutOfScope;
91
92 // Collect all variables in this scope
93
2/2
✓ Branch 0 (24→6) taken 29036 times.
✓ Branch 1 (24→25) taken 16706 times.
45742 for (const auto &[name, entry] : symbolTable.symbols) {
94 // Skip 'this' and result variables
95
8/10
✓ Branch 0 (9→10) taken 29036 times.
✗ Branch 1 (9→48) not taken.
✓ Branch 2 (10→11) taken 23591 times.
✓ Branch 3 (10→13) taken 5445 times.
✓ Branch 4 (11→12) taken 23591 times.
✗ Branch 5 (11→48) not taken.
✓ Branch 6 (12→13) taken 6115 times.
✓ Branch 7 (12→14) taken 17476 times.
✓ Branch 8 (15→16) taken 11560 times.
✓ Branch 9 (15→17) taken 17476 times.
29036 if (name == THIS_VARIABLE_NAME || name == RETURN_VARIABLE_NAME)
96 11560 continue;
97 // Skip parameters (ToDo: Remove when copy constructors work for by-value argument passing)
98
2/2
✓ Branch 0 (17→18) taken 9968 times.
✓ Branch 1 (17→19) taken 7508 times.
17476 if (entry.isParam)
99 9968 continue;
100 // Found variable, that goes out of scope
101
2/4
✓ Branch 0 (19→20) taken 7508 times.
✗ Branch 1 (19→47) not taken.
✓ Branch 2 (20→21) taken 7508 times.
✗ Branch 3 (20→47) not taken.
7508 varsGoingOutOfScope.push_back(&symbolTable.symbols.at(name));
102 }
103
104 // If this is the scope of a dtor, also return all fields of the struct
105
2/2
✓ Branch 0 (25→26) taken 100 times.
✓ Branch 1 (25→45) taken 16606 times.
16706 if (isDtorScope) {
106
2/4
✓ Branch 0 (26→27) taken 100 times.
✗ Branch 1 (26→29) not taken.
✓ Branch 2 (27→28) taken 100 times.
✗ Branch 3 (27→29) not taken.
100 assert(parent != nullptr && parent->type == ScopeType::STRUCT);
107 // Get all fields of the struct
108
2/2
✓ Branch 0 (43→32) taken 3101 times.
✓ Branch 1 (43→44) taken 100 times.
3201 for (const auto &[name, entry] : parent->symbolTable.symbols)
109
4/6
✓ Branch 0 (35→36) taken 3101 times.
✗ Branch 1 (35→51) not taken.
✓ Branch 2 (36→37) taken 3101 times.
✗ Branch 3 (36→49) not taken.
✓ Branch 4 (37→38) taken 271 times.
✓ Branch 5 (37→41) taken 2830 times.
3101 if (!entry.getQualType().isOneOf({TY_FUNCTION, TY_PROCEDURE}))
110
2/4
✓ Branch 0 (38→39) taken 271 times.
✗ Branch 1 (38→50) not taken.
✓ Branch 2 (39→40) taken 271 times.
✗ Branch 3 (39→50) not taken.
271 varsGoingOutOfScope.push_back(&parent->symbolTable.symbols.at(name));
111 }
112
113 16706 return varsGoingOutOfScope;
114 }
115
116 /**
117 * Insert a new generic type in this scope
118 *
119 * @param typeName Generic type name
120 * @param genericType Generic type itself
121 */
122 2765 void Scope::insertGenericType(const std::string &typeName, const GenericType &genericType) {
123
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 2765 times.
2765 assert(!genericTypes.contains(typeName));
124
2/4
✓ Branch 0 (5→6) taken 2765 times.
✗ Branch 1 (5→11) not taken.
✓ Branch 2 (6→7) taken 2765 times.
✗ Branch 3 (6→9) not taken.
2765 genericTypes.insert({typeName, genericType});
125 2765 }
126
127 /**
128 * Search for a generic type by its name. If it was not found, the parent scopes will be searched.
129 * If the generic type does not exist at all, the function will return a nullptr.
130 *
131 * @param typeName Name of the generic type
132 * @return Generic type
133 */
134 27837 GenericType *Scope::lookupGenericType(const std::string &typeName) { // NOLINT(misc-no-recursion)
135
2/2
✓ Branch 0 (3→4) taken 9184 times.
✓ Branch 1 (3→6) taken 18653 times.
27837 if (genericTypes.contains(typeName))
136 9184 return &genericTypes.at(typeName);
137
2/2
✓ Branch 0 (6→7) taken 1906 times.
✓ Branch 1 (6→9) taken 16747 times.
18653 return parent ? parent->lookupGenericType(typeName) : nullptr;
138 }
139
140 /**
141 * Collect all warnings, produced within this scope
142 *
143 * @param warnings List of warnings
144 * @return Collection of warnings
145 */
146 1382 void Scope::collectWarnings(std::vector<CompilerWarning> &warnings) const { // NOLINT(misc-no-recursion)
147 // Visit own symbols
148 CompilerWarningType warningType;
149 1382 std::string warningMessage;
150
5/8
✓ Branch 0 (3→4) taken 1382 times.
✗ Branch 1 (3→211) not taken.
✓ Branch 2 (4→5) taken 1382 times.
✗ Branch 3 (4→211) not taken.
✓ Branch 4 (5→6) taken 1382 times.
✗ Branch 5 (5→211) not taken.
✓ Branch 6 (140→7) taken 2964 times.
✓ Branch 7 (140→141) taken 1382 times.
4346 for (const auto &entry : symbolTable.symbols | std::views::values) {
151 // Do not produce a warning if the symbol is used or has a special name
152 2964 const std::string &name = entry.name;
153
6/6
✓ Branch 0 (8→9) taken 229 times.
✓ Branch 1 (8→11) taken 2735 times.
✓ Branch 2 (10→11) taken 60 times.
✓ Branch 3 (10→12) taken 169 times.
✓ Branch 4 (13→14) taken 2795 times.
✓ Branch 5 (13→15) taken 169 times.
2964 if (entry.used || name.starts_with(UNUSED_VARIABLE_NAME))
154 2818 continue;
155
156
1/2
✓ Branch 0 (15→16) taken 169 times.
✗ Branch 1 (15→210) not taken.
169 const QualType entryType = entry.getQualType();
157
158 // Determine warning type and message by the scope type and the symbol type
159
4/5
✓ Branch 0 (16→17) taken 28 times.
✓ Branch 1 (16→92) taken 51 times.
✓ Branch 2 (16→119) taken 37 times.
✗ Branch 3 (16→125) not taken.
✓ Branch 4 (16→128) taken 53 times.
169 switch (type) {
160 28 case ScopeType::GLOBAL: {
161 // Skip generic function/procedure/struct/interface entries
162
6/10
✓ Branch 0 (17→18) taken 28 times.
✗ Branch 1 (17→155) not taken.
✓ Branch 2 (18→19) taken 16 times.
✓ Branch 3 (18→23) taken 12 times.
✓ Branch 4 (19→20) taken 16 times.
✗ Branch 5 (19→155) not taken.
✗ Branch 6 (21→22) not taken.
✓ Branch 7 (21→23) taken 16 times.
✗ Branch 8 (24→25) not taken.
✓ Branch 9 (24→26) taken 28 times.
28 if (entryType.isOneOf({TY_FUNCTION, TY_PROCEDURE, TY_STRUCT, TY_INTERFACE}) && !entryType.getTemplateTypes().empty())
163 continue;
164
165
3/4
✓ Branch 0 (26→27) taken 28 times.
✗ Branch 1 (26→210) not taken.
✓ Branch 2 (27→28) taken 11 times.
✓ Branch 3 (27→38) taken 17 times.
28 if (entryType.is(TY_FUNCTION)) {
166
1/2
✓ Branch 0 (28→29) taken 11 times.
✗ Branch 1 (28→210) not taken.
11 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
167 11 warningType = UNUSED_FUNCTION;
168
3/6
✓ Branch 0 (30→31) taken 11 times.
✗ Branch 1 (30→160) not taken.
✓ Branch 2 (31→32) taken 11 times.
✗ Branch 3 (31→158) not taken.
✓ Branch 4 (32→33) taken 11 times.
✗ Branch 5 (32→156) not taken.
11 warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused";
169
3/4
✓ Branch 0 (38→39) taken 17 times.
✗ Branch 1 (38→210) not taken.
✓ Branch 2 (39→40) taken 2 times.
✓ Branch 3 (39→50) taken 15 times.
17 } else if (entryType.is(TY_PROCEDURE)) {
170
1/2
✓ Branch 0 (40→41) taken 2 times.
✗ Branch 1 (40→210) not taken.
2 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
171 2 warningType = UNUSED_PROCEDURE;
172
3/6
✓ Branch 0 (42→43) taken 2 times.
✗ Branch 1 (42→167) not taken.
✓ Branch 2 (43→44) taken 2 times.
✗ Branch 3 (43→165) not taken.
✓ Branch 4 (44→45) taken 2 times.
✗ Branch 5 (44→163) not taken.
2 warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused";
173
3/4
✓ Branch 0 (50→51) taken 15 times.
✗ Branch 1 (50→210) not taken.
✓ Branch 2 (51→52) taken 2 times.
✓ Branch 3 (51→58) taken 13 times.
15 } else if (entryType.is(TY_STRUCT)) {
174 2 warningType = UNUSED_STRUCT;
175
2/4
✓ Branch 0 (52→53) taken 2 times.
✗ Branch 1 (52→172) not taken.
✓ Branch 2 (53→54) taken 2 times.
✗ Branch 3 (53→170) not taken.
2 warningMessage = "The struct '" + entry.name + "' is unused";
176
3/4
✓ Branch 0 (58→59) taken 13 times.
✗ Branch 1 (58→210) not taken.
✓ Branch 2 (59→60) taken 1 times.
✓ Branch 3 (59→66) taken 12 times.
13 } else if (entryType.is(TY_INTERFACE)) {
177 1 warningType = UNUSED_INTERFACE;
178
2/4
✓ Branch 0 (60→61) taken 1 times.
✗ Branch 1 (60→176) not taken.
✓ Branch 2 (61→62) taken 1 times.
✗ Branch 3 (61→174) not taken.
1 warningMessage = "The interface '" + entry.name + "' is unused";
179
3/4
✓ Branch 0 (66→67) taken 12 times.
✗ Branch 1 (66→210) not taken.
✓ Branch 2 (67→68) taken 2 times.
✓ Branch 3 (67→69) taken 10 times.
12 } else if (entryType.is(TY_ENUM)) {
180 2 continue; // Do not report unused enums. Only unused enum items are reported
181
3/4
✓ Branch 0 (69→70) taken 10 times.
✗ Branch 1 (69→210) not taken.
✓ Branch 2 (70→71) taken 4 times.
✓ Branch 3 (70→77) taken 6 times.
10 } else if (entryType.is(TY_IMPORT)) {
182 4 warningType = UNUSED_IMPORT;
183
2/4
✓ Branch 0 (71→72) taken 4 times.
✗ Branch 1 (71→180) not taken.
✓ Branch 2 (72→73) taken 4 times.
✗ Branch 3 (72→178) not taken.
4 warningMessage = "The import '" + entry.name + "' is unused";
184
3/4
✓ Branch 0 (77→78) taken 6 times.
✗ Branch 1 (77→210) not taken.
✓ Branch 2 (78→79) taken 1 times.
✓ Branch 3 (78→85) taken 5 times.
6 } else if (entryType.is(TY_ALIAS)) {
185 1 warningType = UNUSED_ALIAS;
186
2/4
✓ Branch 0 (79→80) taken 1 times.
✗ Branch 1 (79→184) not taken.
✓ Branch 2 (80→81) taken 1 times.
✗ Branch 3 (80→182) not taken.
1 warningMessage = "The type alias '" + entry.name + "' is unused";
187 } else {
188 5 warningType = UNUSED_VARIABLE;
189
2/4
✓ Branch 0 (85→86) taken 5 times.
✗ Branch 1 (85→188) not taken.
✓ Branch 2 (86→87) taken 5 times.
✗ Branch 3 (86→186) not taken.
5 warningMessage = "The variable '" + entry.name + "' is unused";
190 }
191
192 26 break;
193 }
194 51 case ScopeType::STRUCT: // fall-through
195 case ScopeType::INTERFACE: {
196
3/4
✓ Branch 0 (92→93) taken 51 times.
✗ Branch 1 (92→190) not taken.
✓ Branch 2 (93→94) taken 40 times.
✓ Branch 3 (93→112) taken 11 times.
51 if (entryType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) {
197 // Skip implicit method entries and generic templates
198
1/2
✓ Branch 0 (94→95) taken 40 times.
✗ Branch 1 (94→210) not taken.
40 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
199
5/6
✓ Branch 0 (96→97) taken 19 times.
✓ Branch 1 (96→99) taken 21 times.
✗ Branch 2 (98→99) not taken.
✓ Branch 3 (98→100) taken 19 times.
✓ Branch 4 (101→102) taken 21 times.
✓ Branch 5 (101→103) taken 19 times.
40 if (fctManifestations->empty() || fctManifestations->front()->implicitDefault)
200 21 continue;
201
202 19 warningType = UNUSED_METHOD;
203
3/6
✓ Branch 0 (104→105) taken 19 times.
✗ Branch 1 (104→195) not taken.
✓ Branch 2 (105→106) taken 19 times.
✗ Branch 3 (105→193) not taken.
✓ Branch 4 (106→107) taken 19 times.
✗ Branch 5 (106→191) not taken.
19 warningMessage = "The method '" + fctManifestations->front()->getSignature() + "' is unused";
204 } else {
205 11 warningType = UNUSED_FIELD;
206
2/4
✓ Branch 0 (112→113) taken 11 times.
✗ Branch 1 (112→200) not taken.
✓ Branch 2 (113→114) taken 11 times.
✗ Branch 3 (113→198) not taken.
11 warningMessage = "The field '" + entry.name + "' is unused";
207 }
208 30 break;
209 }
210 37 case ScopeType::ENUM: {
211 37 warningType = UNUSED_ENUM_ITEM;
212
2/4
✓ Branch 0 (119→120) taken 37 times.
✗ Branch 1 (119→204) not taken.
✓ Branch 2 (120→121) taken 37 times.
✗ Branch 3 (120→202) not taken.
37 warningMessage = "The enum item '" + entry.name + "' is unused";
213 37 break;
214 }
215 case ScopeType::FOREACH_BODY: {
216 // Skip idx variables
217 if (entry.name == FOREACH_DEFAULT_IDX_VARIABLE_NAME)
218 continue;
219 [[fallthrough]];
220 }
221 default: {
222 53 warningType = UNUSED_VARIABLE;
223
2/4
✓ Branch 0 (128→129) taken 53 times.
✗ Branch 1 (128→208) not taken.
✓ Branch 2 (129→130) taken 53 times.
✗ Branch 3 (129→206) not taken.
53 warningMessage = "The variable '" + entry.name + "' is unused";
224 53 break;
225 }
226 }
227
228 // Add warning
229
2/4
✓ Branch 0 (134→135) taken 146 times.
✗ Branch 1 (134→210) not taken.
✓ Branch 2 (135→136) taken 146 times.
✗ Branch 3 (135→210) not taken.
146 warnings.emplace_back(entry.getDeclCodeLoc(), warningType, warningMessage);
230 }
231
232 // Visit children
233
5/8
✓ Branch 0 (141→142) taken 1382 times.
✗ Branch 1 (141→212) not taken.
✓ Branch 2 (142→143) taken 1382 times.
✗ Branch 3 (142→212) not taken.
✓ Branch 4 (143→144) taken 1382 times.
✗ Branch 5 (143→212) not taken.
✓ Branch 6 (152→145) taken 1163 times.
✓ Branch 7 (152→153) taken 1382 times.
2545 for (const auto &childScope : children | std::views::values)
234
2/2
✓ Branch 0 (147→148) taken 1128 times.
✓ Branch 1 (147→150) taken 35 times.
1163 if (!childScope->isGenericScope)
235
1/2
✓ Branch 0 (149→150) taken 1128 times.
✗ Branch 1 (149→212) not taken.
1128 childScope->collectWarnings(warnings);
236 1382 }
237
238 /**
239 * Checks if all variables of this and all child scopes are of an explicit type.
240 * This is executed after type inference to check that all variables could be inferred correctly.
241 */
242 53880 void Scope::ensureSuccessfulTypeInference() const { // NOLINT(misc-no-recursion)
243 // Check symbols in this scope
244
2/2
✓ Branch 0 (19→4) taken 121811 times.
✓ Branch 1 (19→20) taken 53879 times.
175690 for (auto &[name, entry] : symbolTable.symbols)
245
4/6
✓ Branch 0 (7→8) taken 121811 times.
✗ Branch 1 (7→40) not taken.
✓ Branch 2 (8→9) taken 121811 times.
✗ Branch 3 (8→40) not taken.
✓ Branch 4 (9→10) taken 1 times.
✓ Branch 5 (9→17) taken 121810 times.
121811 if (entry.getQualType().is(TY_DYN))
246
3/6
✓ Branch 0 (11→12) taken 1 times.
✗ Branch 1 (11→36) not taken.
✓ Branch 2 (12→13) taken 1 times.
✗ Branch 3 (12→34) not taken.
✓ Branch 4 (13→14) taken 1 times.
✗ Branch 5 (13→31) not taken.
1 throw SemanticError(entry.declNode, UNEXPECTED_DYN_TYPE, "For the variable '" + name + "' no type could be inferred");
247
248 // Check child scopes
249
5/8
✓ Branch 0 (20→21) taken 53879 times.
✗ Branch 1 (20→41) not taken.
✓ Branch 2 (21→22) taken 53879 times.
✗ Branch 3 (21→41) not taken.
✓ Branch 4 (22→23) taken 53879 times.
✗ Branch 5 (22→41) not taken.
✓ Branch 6 (29→24) taken 52232 times.
✓ Branch 7 (29→30) taken 53878 times.
106110 for (const auto &scope : children | std::views::values)
250
2/2
✓ Branch 0 (26→27) taken 52231 times.
✓ Branch 1 (26→41) taken 1 times.
52232 scope->ensureSuccessfulTypeInference();
251 53878 }
252
253 /**
254 * Get the number of fields if this is a struct scope
255 *
256 * @return Number of fields
257 */
258 40635 size_t Scope::getFieldCount() const {
259
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 40635 times.
40635 assert(type == ScopeType::STRUCT);
260 40635 size_t fieldCount = 0;
261
5/8
✓ Branch 0 (4→5) taken 40635 times.
✗ Branch 1 (4→29) not taken.
✓ Branch 2 (5→6) taken 40635 times.
✗ Branch 3 (5→29) not taken.
✓ Branch 4 (6→7) taken 40635 times.
✗ Branch 5 (6→29) not taken.
✓ Branch 6 (26→8) taken 776641 times.
✓ Branch 7 (26→27) taken 40635 times.
817276 for (const auto &symbol : symbolTable.symbols | std::views::values) {
262
1/2
✗ Branch 0 (9→10) not taken.
✓ Branch 1 (9→11) taken 776641 times.
776641 if (symbol.anonymous)
263 continue;
264
1/2
✓ Branch 0 (11→12) taken 776641 times.
✗ Branch 1 (11→29) not taken.
776641 const QualType &symbolType = symbol.getQualType();
265
2/4
✓ Branch 0 (12→13) taken 776641 times.
✗ Branch 1 (12→29) not taken.
✗ Branch 2 (13→14) not taken.
✓ Branch 3 (13→15) taken 776641 times.
776641 if (symbolType.is(TY_IMPORT))
266 continue;
267 776641 const ASTNode *declNode = symbol.declNode;
268
8/10
✓ Branch 0 (15→16) taken 776641 times.
✗ Branch 1 (15→29) not taken.
✓ Branch 2 (16→17) taken 165362 times.
✓ Branch 3 (16→19) taken 611279 times.
✓ Branch 4 (17→18) taken 165362 times.
✗ Branch 5 (17→29) not taken.
✓ Branch 6 (18→19) taken 29231 times.
✓ Branch 7 (18→20) taken 136131 times.
✓ Branch 8 (21→22) taken 640510 times.
✓ Branch 9 (21→23) taken 136131 times.
776641 if (declNode->isFctOrProcDef() || declNode->isStructDef())
269 640510 continue;
270 136131 fieldCount++;
271 }
272 40635 return fieldCount;
273 }
274
275 /**
276 * Get all virtual methods in this scope, sorted by declaration code location
277 *
278 * @return List of virtual method pointers
279 */
280 550 std::vector<Function *> Scope::getVirtualMethods() {
281
3/4
✓ Branch 0 (2→3) taken 240 times.
✓ Branch 1 (2→5) taken 310 times.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 240 times.
550 assert(type == ScopeType::STRUCT || type == ScopeType::INTERFACE);
282
283 // Collect all virtual methods
284 550 std::vector<Function *> methods;
285
2/2
✓ Branch 0 (37→7) taken 3630 times.
✓ Branch 1 (37→38) taken 550 times.
4180 for (auto &[fctId, manifestationList] : functions) {
286
1/2
✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→13) taken 3630 times.
3630 assert(!manifestationList.empty());
287
2/2
✓ Branch 0 (34→15) taken 5286 times.
✓ Branch 1 (34→35) taken 3630 times.
8916 for (auto &[mangledName, function] : manifestationList)
288
2/2
✓ Branch 0 (27→28) taken 1220 times.
✓ Branch 1 (27→32) taken 4066 times.
5286 if (function.isVirtualMethod())
289
3/6
✓ Branch 0 (28→29) taken 1220 times.
✗ Branch 1 (28→41) not taken.
✓ Branch 2 (29→30) taken 1220 times.
✗ Branch 3 (29→41) not taken.
✓ Branch 4 (30→31) taken 1220 times.
✗ Branch 5 (30→41) not taken.
1220 methods.push_back(&functions.at(fctId).at(mangledName));
290 }
291
292 // Sort the list
293
2/4
✓ Branch 0 (38→39) taken 550 times.
✗ Branch 1 (38→44) not taken.
✗ Branch 2 (4→5) not taken.
✓ Branch 3 (4→6) taken 1492 times.
3534 std::ranges::sort(methods, [](const Function *a, const Function *b) { return a->getDeclCodeLoc() < b->getDeclCodeLoc(); });
294
295 550 return methods;
296 }
297
298 /**
299 * Retrieve all struct manifestations in this scope in the order of their declaration
300 *
301 * @return All struct manifestations in declaration order
302 */
303 974 std::vector<const Struct *> Scope::getAllStructManifestationsInDeclarationOrder() const {
304 // Retrieve all struct manifestations in this scope
305 974 std::vector<const Struct *> manifestations;
306
1/2
✓ Branch 0 (3→4) taken 974 times.
✗ Branch 1 (3→27) not taken.
974 manifestations.reserve(structs.size()); // Reserve at least the size of individual generic structs
307
5/8
✓ Branch 0 (4→5) taken 974 times.
✗ Branch 1 (4→26) not taken.
✓ Branch 2 (5→6) taken 974 times.
✗ Branch 3 (5→26) not taken.
✓ Branch 4 (6→7) taken 974 times.
✗ Branch 5 (6→26) not taken.
✓ Branch 6 (20→8) taken 579 times.
✓ Branch 7 (20→21) taken 974 times.
1553 for (const auto &structManifestations : structs | std::views::values)
308
5/8
✓ Branch 0 (9→10) taken 579 times.
✗ Branch 1 (9→25) not taken.
✓ Branch 2 (10→11) taken 579 times.
✗ Branch 3 (10→25) not taken.
✓ Branch 4 (11→12) taken 579 times.
✗ Branch 5 (11→25) not taken.
✓ Branch 6 (17→13) taken 580 times.
✓ Branch 7 (17→18) taken 579 times.
1159 for (const auto &manifestation : structManifestations | std::views::values)
309
1/2
✓ Branch 0 (14→15) taken 580 times.
✗ Branch 1 (14→24) not taken.
580 manifestations.push_back(&manifestation);
310
311 // Sort manifestations by declaration code location
312
2/2
✓ Branch 0 (4→5) taken 2 times.
✓ Branch 1 (4→6) taken 283 times.
570 auto sortLambda = [](const Struct *lhs, const Struct *rhs) { return lhs->getDeclCodeLoc() < rhs->getDeclCodeLoc(); };
313
1/2
✓ Branch 0 (21→22) taken 974 times.
✗ Branch 1 (21→27) not taken.
974 std::ranges::sort(manifestations, sortLambda);
314 974 return manifestations;
315 }
316
317 /**
318 * Check if this struct has any reference fields
319 *
320 * @return Has reference fields or not
321 */
322 298 bool Scope::hasRefFields() {
323
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 298 times.
298 assert(type == ScopeType::STRUCT);
324
2/2
✓ Branch 0 (16→5) taken 788 times.
✓ Branch 1 (16→17) taken 291 times.
1079 for (size_t i = 0; i < getFieldCount(); i++)
325
3/4
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 788 times.
✓ Branch 2 (12→13) taken 7 times.
✓ Branch 3 (12→14) taken 781 times.
1576 if (lookupField(i)->getQualType().isRef())
326 7 return true;
327 291 return false;
328 }
329
330 /**
331 * Get the current number of nested loops
332 *
333 * @return Number of loops
334 */
335 2484 unsigned int Scope::getLoopNestingDepth() const { // NOLINT(misc-no-recursion)
336
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 2484 times.
2484 assert(parent != nullptr);
337
2/2
✓ Branch 0 (4→5) taken 454 times.
✓ Branch 1 (4→6) taken 2030 times.
2484 if (parent->parent == nullptr)
338 454 return 0;
339 2030 unsigned int loopCount = parent->getLoopNestingDepth();
340
6/6
✓ Branch 0 (7→8) taken 1927 times.
✓ Branch 1 (7→10) taken 103 times.
✓ Branch 2 (8→9) taken 1245 times.
✓ Branch 3 (8→10) taken 682 times.
✓ Branch 4 (9→10) taken 24 times.
✓ Branch 5 (9→11) taken 1221 times.
2030 if (type == ScopeType::WHILE_BODY || type == ScopeType::FOR_BODY || type == ScopeType::FOREACH_BODY)
341 809 loopCount++;
342 2030 return loopCount;
343 }
344
345 /**
346 * Check if this scope is one of the child scopes of a switch statement
347 *
348 * @return Child scope of switch statement or not
349 */
350 7 bool Scope::isInCaseBranch() const { // NOLINT(misc-no-recursion)
351
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 7 times.
7 assert(parent != nullptr);
352
2/2
✓ Branch 0 (4→5) taken 2 times.
✓ Branch 1 (4→6) taken 5 times.
7 if (parent->parent == nullptr)
353 2 return false;
354
2/2
✓ Branch 0 (6→7) taken 4 times.
✓ Branch 1 (6→8) taken 1 times.
5 if (type == ScopeType::CASE_BODY)
355 4 return true;
356 1 return parent->isInCaseBranch();
357 }
358
359 /**
360 * Check if this scope is within an async scope
361 *
362 * @return Within async scope or not
363 */
364 75 bool Scope::isInAsyncScope() const { // NOLINT(misc-no-recursion)
365
2/2
✓ Branch 0 (2→3) taken 8 times.
✓ Branch 1 (2→4) taken 67 times.
75 if (isAsyncScope)
366 8 return true;
367
3/4
✓ Branch 0 (4→5) taken 43 times.
✓ Branch 1 (4→8) taken 24 times.
✗ Branch 2 (6→7) not taken.
✓ Branch 3 (6→8) taken 43 times.
67 return parent != nullptr && parent->isInAsyncScope();
368 }
369
370 /**
371 * Check if unsafe operations are allowed in this scope
372 *
373 * @return Allowed or not
374 */
375 4593 bool Scope::doesAllowUnsafeOperations() const { // NOLINT(misc-no-recursion)
376
2/2
✓ Branch 0 (2→3) taken 2882 times.
✓ Branch 1 (2→4) taken 1711 times.
4593 if (type == ScopeType::UNSAFE_BODY)
377 2882 return true;
378
4/4
✓ Branch 0 (4→5) taken 1710 times.
✓ Branch 1 (4→8) taken 1 times.
✓ Branch 2 (6→7) taken 1709 times.
✓ Branch 3 (6→8) taken 1 times.
1711 return parent != nullptr && parent->doesAllowUnsafeOperations();
379 }
380
381 /**
382 * Checks if this scope is imported
383 *
384 * @param askingScope Scope, which asks whether the current one is imported from its point of view or not
385 *
386 * @return Imported / not imported
387 */
388 78889 bool Scope::isImportedBy(const Scope *askingScope) const { return askingScope->sourceFile->imports(sourceFile); }
389
390 /**
391 * Get JSON representation of the symbol table
392 *
393 * @return Symbol table as JSON object
394 */
395 53944 nlohmann::json Scope::getSymbolTableJSON() const { // NOLINT(misc-no-recursion)
396
1/2
✓ Branch 0 (2→3) taken 53944 times.
✗ Branch 1 (2→42) not taken.
53944 nlohmann::json result = symbolTable.toJSON();
397
398 // Collect all children
399 53944 std::vector<nlohmann::json> jsonChildren;
400
1/2
✓ Branch 0 (4→5) taken 53944 times.
✗ Branch 1 (4→38) not taken.
53944 jsonChildren.reserve(children.size());
401
2/2
✓ Branch 0 (20→7) taken 52288 times.
✓ Branch 1 (20→21) taken 53944 times.
106232 for (const auto &[name, childScope] : children) {
402
1/2
✓ Branch 0 (11→12) taken 52288 times.
✗ Branch 1 (11→33) not taken.
52288 nlohmann::json c = childScope->getSymbolTableJSON();
403
2/4
✓ Branch 0 (12→13) taken 52288 times.
✗ Branch 1 (12→30) not taken.
✓ Branch 2 (13→14) taken 52288 times.
✗ Branch 3 (13→28) not taken.
52288 c["name"] = name; // Inject symbol table name into JSON object
404
1/2
✓ Branch 0 (16→17) taken 52288 times.
✗ Branch 1 (16→31) not taken.
52288 jsonChildren.emplace_back(c);
405 52288 }
406
2/4
✓ Branch 0 (21→22) taken 53944 times.
✗ Branch 1 (21→37) not taken.
✓ Branch 2 (22→23) taken 53944 times.
✗ Branch 3 (22→35) not taken.
53944 result["children"] = jsonChildren;
407
408 53944 return result;
409 53944 }
410
411 } // namespace spice::compiler
412