GCC Code Coverage Report


Directory: ../
File: src/symboltablebuilder/Scope.cpp
Date: 2025-10-23 00:48:11
Coverage Exec Excl Total
Lines: 95.0% 189 0 199
Functions: 100.0% 22 0 22
Branches: 64.4% 261 0 405

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 22798 Scope *Scope::createChildScope(const std::string &scopeName, ScopeType scopeType, const CodeLoc *declCodeLoc) {
21
3/6
✓ Branch 2 → 3 taken 22798 times.
✗ Branch 2 → 14 not taken.
✓ Branch 3 → 4 taken 22798 times.
✗ Branch 3 → 12 not taken.
✓ Branch 4 → 5 taken 22798 times.
✗ Branch 4 → 10 not taken.
22798 children.insert({scopeName, std::make_shared<Scope>(this, sourceFile, scopeType, declCodeLoc)});
22 22798 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 12230 void Scope::renameChildScope(const std::string &oldName, const std::string &newName) {
33
4/8
✓ Branch 2 → 3 taken 12230 times.
✗ Branch 2 → 19 not taken.
✓ Branch 3 → 4 taken 12230 times.
✗ Branch 3 → 7 not taken.
✓ Branch 4 → 5 taken 12230 times.
✗ Branch 4 → 19 not taken.
✓ Branch 5 → 6 taken 12230 times.
✗ Branch 5 → 7 not taken.
12230 assert(children.contains(oldName) && !children.contains(newName));
34
1/2
✓ Branch 8 → 9 taken 12230 times.
✗ Branch 8 → 19 not taken.
12230 auto nodeHandler = children.extract(oldName);
35
1/2
✓ Branch 10 → 11 taken 12230 times.
✗ Branch 10 → 17 not taken.
12230 nodeHandler.key() = newName;
36
1/2
✓ Branch 12 → 13 taken 12230 times.
✗ Branch 12 → 16 not taken.
12230 children.insert(std::move(nodeHandler));
37 12230 }
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 3620 Scope *Scope::copyChildScope(const std::string &oldName, const std::string &newName) {
46
4/8
✓ Branch 2 → 3 taken 3620 times.
✗ Branch 2 → 23 not taken.
✓ Branch 3 → 4 taken 3620 times.
✗ Branch 3 → 7 not taken.
✓ Branch 4 → 5 taken 3620 times.
✗ Branch 4 → 23 not taken.
✓ Branch 5 → 6 taken 3620 times.
✗ Branch 5 → 7 not taken.
3620 assert(children.contains(oldName) && !children.contains(newName));
47 // Create copy
48
2/4
✓ Branch 8 → 9 taken 3620 times.
✗ Branch 8 → 23 not taken.
✓ Branch 10 → 11 taken 3620 times.
✗ Branch 10 → 23 not taken.
3620 const std::shared_ptr<Scope> newScope = children.at(oldName)->deepCopyScope();
49 // Save copy under new name
50
2/4
✓ Branch 11 → 12 taken 3620 times.
✗ Branch 11 → 20 not taken.
✓ Branch 12 → 13 taken 3620 times.
✗ Branch 12 → 18 not taken.
3620 children.insert({newName, newScope});
51 7240 return newScope.get();
52 3620 }
53
54 /**
55 * Deep copy the current scope and all its children
56 *
57 * @return Deep copy of the current scope
58 */
59 12867 std::shared_ptr<Scope> Scope::deepCopyScope() { // NOLINT(misc-no-recursion)
60 12867 const std::shared_ptr<Scope> newScope = std::make_shared<Scope>(*this);
61
2/2
✓ Branch 30 → 5 taken 9247 times.
✓ Branch 30 → 31 taken 12867 times.
22114 for (const auto &[childName, oldChild] : children) {
62
2/4
✓ Branch 9 → 10 taken 9247 times.
✗ Branch 9 → 37 not taken.
✓ Branch 11 → 12 taken 9247 times.
✗ Branch 11 → 35 not taken.
9247 newScope->children[childName] = oldChild->deepCopyScope();
63
1/2
✓ Branch 16 → 17 taken 9247 times.
✗ Branch 16 → 38 not taken.
9247 newScope->children[childName]->parent = newScope.get();
64
2/4
✓ Branch 19 → 20 taken 9247 times.
✗ Branch 19 → 38 not taken.
✓ Branch 22 → 23 taken 9247 times.
✗ Branch 22 → 38 not taken.
9247 newScope->children[childName]->symbolTable.scope = newScope->children[childName].get();
65
1/2
✓ Branch 26 → 27 taken 9247 times.
✗ Branch 26 → 38 not taken.
9247 newScope->children[childName]->symbolTable.parent = &newScope->symbolTable;
66 }
67 12867 newScope->symbolTable.scope = newScope.get();
68 12867 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 44781 Scope *Scope::getChildScope(const std::string &scopeName) const {
78
5/6
✓ Branch 3 → 4 taken 44780 times.
✓ Branch 3 → 7 taken 1 time.
✓ Branch 5 → 6 taken 44780 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 44780 times.
✓ Branch 8 → 11 taken 1 time.
44781 if (!children.empty() && children.contains(scopeName))
79 44780 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 19217 std::vector<SymbolTableEntry *> Scope::getVarsGoingOutOfScope() { // NOLINT(misc-no-recursion)
89
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 19217 times.
19217 assert(!isRootScope()); // Should not be called in root scope
90 19217 std::vector<SymbolTableEntry *> varsGoingOutOfScope;
91
92 // Collect all variables in this scope
93
2/2
✓ Branch 26 → 8 taken 32830 times.
✓ Branch 26 → 27 taken 19217 times.
52047 for (const auto &[name, entry] : symbolTable.symbols) {
94 // Skip 'this' and result variables
95
8/10
✓ Branch 11 → 12 taken 32830 times.
✗ Branch 11 → 52 not taken.
✓ Branch 12 → 13 taken 26815 times.
✓ Branch 12 → 15 taken 6015 times.
✓ Branch 13 → 14 taken 26815 times.
✗ Branch 13 → 52 not taken.
✓ Branch 14 → 15 taken 7508 times.
✓ Branch 14 → 16 taken 19307 times.
✓ Branch 17 → 18 taken 13523 times.
✓ Branch 17 → 19 taken 19307 times.
32830 if (name == THIS_VARIABLE_NAME || name == RETURN_VARIABLE_NAME)
96 13523 continue;
97 // Skip parameters (ToDo: Remove when copy constructors work for by-value argument passing)
98
2/2
✓ Branch 19 → 20 taken 11764 times.
✓ Branch 19 → 21 taken 7543 times.
19307 if (entry.isParam)
99 11764 continue;
100 // Found variable, that goes out of scope
101
2/4
✓ Branch 21 → 22 taken 7543 times.
✗ Branch 21 → 51 not taken.
✓ Branch 22 → 23 taken 7543 times.
✗ Branch 22 → 51 not taken.
7543 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 27 → 28 taken 107 times.
✓ Branch 27 → 49 taken 19110 times.
19217 if (isDtorScope) {
106
2/4
✓ Branch 30 → 31 taken 107 times.
✗ Branch 30 → 33 not taken.
✓ Branch 31 → 32 taken 107 times.
✗ Branch 31 → 33 not taken.
107 assert(!isRootScope() && parent->type == ScopeType::STRUCT);
107 // Get all fields of the struct
108
2/2
✓ Branch 47 → 36 taken 3680 times.
✓ Branch 47 → 48 taken 107 times.
3787 for (const auto &[name, entry] : parent->symbolTable.symbols)
109
4/6
✓ Branch 39 → 40 taken 3680 times.
✗ Branch 39 → 55 not taken.
✓ Branch 40 → 41 taken 3680 times.
✗ Branch 40 → 53 not taken.
✓ Branch 41 → 42 taken 291 times.
✓ Branch 41 → 45 taken 3389 times.
3680 if (!entry.getQualType().isOneOf({TY_FUNCTION, TY_PROCEDURE}))
110
2/4
✓ Branch 42 → 43 taken 291 times.
✗ Branch 42 → 54 not taken.
✓ Branch 43 → 44 taken 291 times.
✗ Branch 43 → 54 not taken.
291 varsGoingOutOfScope.push_back(&parent->symbolTable.symbols.at(name));
111 }
112
113 19217 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 3448 void Scope::insertGenericType(const std::string &typeName, const GenericType &genericType) {
123
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3448 times.
3448 assert(!genericTypes.contains(typeName));
124
2/4
✓ Branch 5 → 6 taken 3448 times.
✗ Branch 5 → 11 not taken.
✓ Branch 6 → 7 taken 3448 times.
✗ Branch 6 → 9 not taken.
3448 genericTypes.insert({typeName, genericType});
125 3448 }
126
127 /**
128 * Search for a generic type by its name in this scope.
129 * If the generic type is not found, a nullptr is returned.
130 *
131 * @param typeName Name of the generic type
132 * @return Generic type
133 */
134 31997 GenericType *Scope::lookupGenericTypeStrict(const std::string &typeName) {
135
2/2
✓ Branch 3 → 4 taken 12791 times.
✓ Branch 3 → 6 taken 19206 times.
31997 return genericTypes.contains(typeName) ? &genericTypes.at(typeName) : nullptr;
136 }
137
138 /**
139 * Collect all warnings, produced within this scope
140 *
141 * @param warnings List of warnings
142 * @return Collection of warnings
143 */
144 1433 void Scope::collectWarnings(std::vector<CompilerWarning> &warnings) const { // NOLINT(misc-no-recursion)
145 // Visit own symbols
146
5/8
✓ Branch 2 → 3 taken 1433 times.
✗ Branch 2 → 218 not taken.
✓ Branch 3 → 4 taken 1433 times.
✗ Branch 3 → 218 not taken.
✓ Branch 4 → 5 taken 1433 times.
✗ Branch 4 → 218 not taken.
✓ Branch 146 → 6 taken 3047 times.
✓ Branch 146 → 147 taken 1433 times.
4480 for (const auto &entry : symbolTable.symbols | std::views::values) {
147 // Do not produce a warning if the symbol is used or has a special name
148 3047 const std::string &name = entry.name;
149
6/6
✓ Branch 7 → 8 taken 235 times.
✓ Branch 7 → 10 taken 2812 times.
✓ Branch 9 → 10 taken 60 times.
✓ Branch 9 → 11 taken 175 times.
✓ Branch 12 → 13 taken 2872 times.
✓ Branch 12 → 14 taken 175 times.
3047 if (entry.used || name.starts_with(UNUSED_VARIABLE_NAME))
150 2896 continue;
151
152
1/2
✓ Branch 14 → 15 taken 175 times.
✗ Branch 14 → 217 not taken.
175 const QualType entryType = entry.getQualType();
153
154 // Determine warning type and message by the scope type and the symbol type
155 175 CompilerWarningType warningType = UNUSED_VARIABLE;
156 175 std::string warningMessage;
157
4/5
✓ Branch 16 → 17 taken 29 times.
✓ Branch 16 → 92 taken 52 times.
✓ Branch 16 → 121 taken 37 times.
✗ Branch 16 → 127 not taken.
✓ Branch 16 → 130 taken 57 times.
175 switch (type) {
158 29 case ScopeType::GLOBAL: {
159 // Skip generic function/procedure/struct/interface entries
160
6/10
✓ Branch 17 → 18 taken 29 times.
✗ Branch 17 → 160 not taken.
✓ Branch 18 → 19 taken 17 times.
✓ Branch 18 → 23 taken 12 times.
✓ Branch 19 → 20 taken 17 times.
✗ Branch 19 → 160 not taken.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 17 times.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 29 times.
29 if (entryType.isOneOf({TY_FUNCTION, TY_PROCEDURE, TY_STRUCT, TY_INTERFACE}) && !entryType.getTemplateTypes().empty())
161 continue;
162
163
3/4
✓ Branch 26 → 27 taken 29 times.
✗ Branch 26 → 215 not taken.
✓ Branch 27 → 28 taken 11 times.
✓ Branch 27 → 38 taken 18 times.
29 if (entryType.is(TY_FUNCTION)) {
164
1/2
✓ Branch 28 → 29 taken 11 times.
✗ Branch 28 → 215 not taken.
11 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
165 11 warningType = UNUSED_FUNCTION;
166
3/6
✓ Branch 30 → 31 taken 11 times.
✗ Branch 30 → 165 not taken.
✓ Branch 31 → 32 taken 11 times.
✗ Branch 31 → 163 not taken.
✓ Branch 32 → 33 taken 11 times.
✗ Branch 32 → 161 not taken.
11 warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused";
167
3/4
✓ Branch 38 → 39 taken 18 times.
✗ Branch 38 → 215 not taken.
✓ Branch 39 → 40 taken 3 times.
✓ Branch 39 → 50 taken 15 times.
18 } else if (entryType.is(TY_PROCEDURE)) {
168
1/2
✓ Branch 40 → 41 taken 3 times.
✗ Branch 40 → 215 not taken.
3 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
169 3 warningType = UNUSED_PROCEDURE;
170
3/6
✓ Branch 42 → 43 taken 3 times.
✗ Branch 42 → 172 not taken.
✓ Branch 43 → 44 taken 3 times.
✗ Branch 43 → 170 not taken.
✓ Branch 44 → 45 taken 3 times.
✗ Branch 44 → 168 not taken.
3 warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused";
171
3/4
✓ Branch 50 → 51 taken 15 times.
✗ Branch 50 → 215 not taken.
✓ Branch 51 → 52 taken 2 times.
✓ Branch 51 → 58 taken 13 times.
15 } else if (entryType.is(TY_STRUCT)) {
172 2 warningType = UNUSED_STRUCT;
173
2/4
✓ Branch 52 → 53 taken 2 times.
✗ Branch 52 → 177 not taken.
✓ Branch 53 → 54 taken 2 times.
✗ Branch 53 → 175 not taken.
2 warningMessage = "The struct '" + entry.name + "' is unused";
174
3/4
✓ Branch 58 → 59 taken 13 times.
✗ Branch 58 → 215 not taken.
✓ Branch 59 → 60 taken 1 time.
✓ Branch 59 → 66 taken 12 times.
13 } else if (entryType.is(TY_INTERFACE)) {
175 1 warningType = UNUSED_INTERFACE;
176
2/4
✓ Branch 60 → 61 taken 1 time.
✗ Branch 60 → 181 not taken.
✓ Branch 61 → 62 taken 1 time.
✗ Branch 61 → 179 not taken.
1 warningMessage = "The interface '" + entry.name + "' is unused";
177
3/4
✓ Branch 66 → 67 taken 12 times.
✗ Branch 66 → 215 not taken.
✓ Branch 67 → 68 taken 2 times.
✓ Branch 67 → 69 taken 10 times.
12 } else if (entryType.is(TY_ENUM)) {
178 2 continue; // Do not report unused enums. Only unused enum items are reported
179
3/4
✓ Branch 69 → 70 taken 10 times.
✗ Branch 69 → 215 not taken.
✓ Branch 70 → 71 taken 4 times.
✓ Branch 70 → 77 taken 6 times.
10 } else if (entryType.is(TY_IMPORT)) {
180 4 warningType = UNUSED_IMPORT;
181
2/4
✓ Branch 71 → 72 taken 4 times.
✗ Branch 71 → 185 not taken.
✓ Branch 72 → 73 taken 4 times.
✗ Branch 72 → 183 not taken.
4 warningMessage = "The import '" + entry.name + "' is unused";
182
3/4
✓ Branch 77 → 78 taken 6 times.
✗ Branch 77 → 215 not taken.
✓ Branch 78 → 79 taken 1 time.
✓ Branch 78 → 85 taken 5 times.
6 } else if (entryType.is(TY_ALIAS)) {
183 1 warningType = UNUSED_ALIAS;
184
2/4
✓ Branch 79 → 80 taken 1 time.
✗ Branch 79 → 189 not taken.
✓ Branch 80 → 81 taken 1 time.
✗ Branch 80 → 187 not taken.
1 warningMessage = "The type alias '" + entry.name + "' is unused";
185 } else {
186 5 warningType = UNUSED_VARIABLE;
187
2/4
✓ Branch 85 → 86 taken 5 times.
✗ Branch 85 → 193 not taken.
✓ Branch 86 → 87 taken 5 times.
✗ Branch 86 → 191 not taken.
5 warningMessage = "The variable '" + entry.name + "' is unused";
188 }
189
190 27 break;
191 }
192 52 case ScopeType::STRUCT: // fall-through
193 case ScopeType::INTERFACE: {
194
3/4
✓ Branch 92 → 93 taken 52 times.
✗ Branch 92 → 215 not taken.
✓ Branch 93 → 94 taken 11 times.
✓ Branch 93 → 100 taken 41 times.
52 if (entry.isField()) {
195 11 warningType = UNUSED_FIELD;
196
2/4
✓ Branch 94 → 95 taken 11 times.
✗ Branch 94 → 197 not taken.
✓ Branch 95 → 96 taken 11 times.
✗ Branch 95 → 195 not taken.
11 warningMessage = "The field '" + entry.name + "' is unused";
197
2/4
✓ Branch 100 → 101 taken 41 times.
✗ Branch 100 → 199 not taken.
✓ Branch 101 → 102 taken 41 times.
✗ Branch 101 → 120 not taken.
41 } else if (entryType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) {
198 // Skip implicit method entries and generic templates
199
1/2
✓ Branch 102 → 103 taken 41 times.
✗ Branch 102 → 215 not taken.
41 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
200
5/6
✓ Branch 104 → 105 taken 19 times.
✓ Branch 104 → 107 taken 22 times.
✗ Branch 106 → 107 not taken.
✓ Branch 106 → 108 taken 19 times.
✓ Branch 109 → 110 taken 22 times.
✓ Branch 109 → 111 taken 19 times.
41 if (fctManifestations->empty() || fctManifestations->front()->implicitDefault)
201 22 continue;
202
203 19 warningType = UNUSED_METHOD;
204
3/6
✓ Branch 112 → 113 taken 19 times.
✗ Branch 112 → 204 not taken.
✓ Branch 113 → 114 taken 19 times.
✗ Branch 113 → 202 not taken.
✓ Branch 114 → 115 taken 19 times.
✗ Branch 114 → 200 not taken.
19 warningMessage = "The method '" + fctManifestations->front()->getSignature() + "' is unused";
205 }
206 30 break;
207 }
208 37 case ScopeType::ENUM: {
209 37 warningType = UNUSED_ENUM_ITEM;
210
2/4
✓ Branch 121 → 122 taken 37 times.
✗ Branch 121 → 209 not taken.
✓ Branch 122 → 123 taken 37 times.
✗ Branch 122 → 207 not taken.
37 warningMessage = "The enum item '" + entry.name + "' is unused";
211 37 break;
212 }
213 case ScopeType::FOREACH_BODY: {
214 // Skip idx variables
215 if (entry.name == FOREACH_DEFAULT_IDX_VARIABLE_NAME)
216 continue;
217 [[fallthrough]];
218 }
219 default: {
220 57 warningType = UNUSED_VARIABLE;
221
2/4
✓ Branch 130 → 131 taken 57 times.
✗ Branch 130 → 213 not taken.
✓ Branch 131 → 132 taken 57 times.
✗ Branch 131 → 211 not taken.
57 warningMessage = "The variable '" + entry.name + "' is unused";
222 57 break;
223 }
224 }
225
226 // Add warning
227
2/4
✓ Branch 136 → 137 taken 151 times.
✗ Branch 136 → 215 not taken.
✓ Branch 137 → 138 taken 151 times.
✗ Branch 137 → 215 not taken.
151 warnings.emplace_back(entry.getDeclCodeLoc(), warningType, warningMessage);
228
2/2
✓ Branch 140 → 141 taken 151 times.
✓ Branch 140 → 143 taken 24 times.
175 }
229
230 // Visit children
231
5/8
✓ Branch 147 → 148 taken 1433 times.
✗ Branch 147 → 219 not taken.
✓ Branch 148 → 149 taken 1433 times.
✗ Branch 148 → 219 not taken.
✓ Branch 149 → 150 taken 1433 times.
✗ Branch 149 → 219 not taken.
✓ Branch 158 → 151 taken 1201 times.
✓ Branch 158 → 159 taken 1433 times.
2634 for (const auto &childScope : children | std::views::values)
232
2/2
✓ Branch 153 → 154 taken 1163 times.
✓ Branch 153 → 156 taken 38 times.
1201 if (!childScope->isGenericScope)
233
1/2
✓ Branch 155 → 156 taken 1163 times.
✗ Branch 155 → 219 not taken.
1163 childScope->collectWarnings(warnings);
234 1433 }
235
236 /**
237 * Checks if all variables of this and all child scopes are of an explicit type.
238 * This is executed after type inference to check that all variables could be inferred correctly.
239 */
240 74192 void Scope::ensureSuccessfulTypeInference() const { // NOLINT(misc-no-recursion)
241 // Check symbols in this scope
242
2/2
✓ Branch 19 → 4 taken 165271 times.
✓ Branch 19 → 20 taken 74191 times.
239462 for (auto &[name, entry] : symbolTable.symbols)
243
4/6
✓ Branch 7 → 8 taken 165271 times.
✗ Branch 7 → 40 not taken.
✓ Branch 8 → 9 taken 165271 times.
✗ Branch 8 → 40 not taken.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 17 taken 165270 times.
165271 if (entry.getQualType().is(TY_DYN))
244
3/6
✓ Branch 11 → 12 taken 1 time.
✗ Branch 11 → 36 not taken.
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 34 not taken.
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 31 not taken.
1 throw SemanticError(entry.declNode, UNEXPECTED_DYN_TYPE, "For the variable '" + name + "' no type could be inferred");
245
246 // Check child scopes
247
5/8
✓ Branch 20 → 21 taken 74191 times.
✗ Branch 20 → 41 not taken.
✓ Branch 21 → 22 taken 74191 times.
✗ Branch 21 → 41 not taken.
✓ Branch 22 → 23 taken 74191 times.
✗ Branch 22 → 41 not taken.
✓ Branch 29 → 24 taken 72118 times.
✓ Branch 29 → 30 taken 74190 times.
146308 for (const auto &scope : children | std::views::values)
248
2/2
✓ Branch 26 → 27 taken 72117 times.
✓ Branch 26 → 41 taken 1 time.
72118 scope->ensureSuccessfulTypeInference();
249 74190 }
250
251 /**
252 * Get the number of fields if this is a struct scope
253 *
254 * @return Number of fields
255 */
256 46393 size_t Scope::getFieldCount() const {
257
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 46393 times.
46393 assert(type == ScopeType::STRUCT);
258 46393 size_t fieldCount = 0;
259
5/8
✓ Branch 4 → 5 taken 46393 times.
✗ Branch 4 → 29 not taken.
✓ Branch 5 → 6 taken 46393 times.
✗ Branch 5 → 29 not taken.
✓ Branch 6 → 7 taken 46393 times.
✗ Branch 6 → 29 not taken.
✓ Branch 26 → 8 taken 863348 times.
✓ Branch 26 → 27 taken 46393 times.
909741 for (const auto &symbol : symbolTable.symbols | std::views::values) {
260
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 863348 times.
863348 if (symbol.anonymous)
261 continue;
262
1/2
✓ Branch 11 → 12 taken 863348 times.
✗ Branch 11 → 29 not taken.
863348 const QualType &symbolType = symbol.getQualType();
263
2/4
✓ Branch 12 → 13 taken 863348 times.
✗ Branch 12 → 29 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 863348 times.
863348 if (symbolType.is(TY_IMPORT))
264 continue;
265 863348 const ASTNode *declNode = symbol.declNode;
266
8/10
✓ Branch 15 → 16 taken 863348 times.
✗ Branch 15 → 29 not taken.
✓ Branch 16 → 17 taken 182540 times.
✓ Branch 16 → 19 taken 680808 times.
✓ Branch 17 → 18 taken 182540 times.
✗ Branch 17 → 29 not taken.
✓ Branch 18 → 19 taken 32732 times.
✓ Branch 18 → 20 taken 149808 times.
✓ Branch 21 → 22 taken 713540 times.
✓ Branch 21 → 23 taken 149808 times.
863348 if (declNode->isFctOrProcDef() || declNode->isStructDef())
267 713540 continue;
268 149808 fieldCount++;
269 }
270 46393 return fieldCount;
271 }
272
273 /**
274 * Get all virtual methods in this scope, sorted by declaration code location
275 *
276 * @return List of virtual method pointers
277 */
278 710 std::vector<Function *> Scope::getVirtualMethods() {
279
3/4
✓ Branch 2 → 3 taken 324 times.
✓ Branch 2 → 5 taken 386 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 324 times.
710 assert(type == ScopeType::STRUCT || type == ScopeType::INTERFACE);
280
281 // Collect all virtual methods
282 710 std::vector<Function *> methods;
283
2/2
✓ Branch 37 → 7 taken 4608 times.
✓ Branch 37 → 38 taken 710 times.
5318 for (auto &[fctId, manifestationList] : functions) {
284
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 4608 times.
4608 assert(!manifestationList.empty());
285
2/2
✓ Branch 34 → 15 taken 6726 times.
✓ Branch 34 → 35 taken 4608 times.
11334 for (auto &[mangledName, function] : manifestationList)
286
2/2
✓ Branch 27 → 28 taken 1610 times.
✓ Branch 27 → 32 taken 5116 times.
6726 if (function.isVirtualMethod())
287
3/6
✓ Branch 28 → 29 taken 1610 times.
✗ Branch 28 → 41 not taken.
✓ Branch 29 → 30 taken 1610 times.
✗ Branch 29 → 41 not taken.
✓ Branch 30 → 31 taken 1610 times.
✗ Branch 30 → 41 not taken.
1610 methods.push_back(&functions.at(fctId).at(mangledName));
288 }
289
290 // Sort the list
291
2/4
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1972 times.
✓ Branch 38 → 39 taken 710 times.
✗ Branch 38 → 44 not taken.
4654 std::ranges::sort(methods, [](const Function *a, const Function *b) { return a->getDeclCodeLoc() < b->getDeclCodeLoc(); });
292
293 710 return methods;
294 }
295
296 /**
297 * Retrieve all struct manifestations in this scope in the order of their declaration
298 *
299 * @return All struct manifestations in declaration order
300 */
301 1084 std::vector<const Struct *> Scope::getAllStructManifestationsInDeclarationOrder() const {
302 // Retrieve all struct manifestations in this scope
303 1084 std::vector<const Struct *> manifestations;
304
1/2
✓ Branch 3 → 4 taken 1084 times.
✗ Branch 3 → 27 not taken.
1084 manifestations.reserve(structs.size()); // Reserve at least the size of individual generic structs
305
5/8
✓ Branch 4 → 5 taken 1084 times.
✗ Branch 4 → 26 not taken.
✓ Branch 5 → 6 taken 1084 times.
✗ Branch 5 → 26 not taken.
✓ Branch 6 → 7 taken 1084 times.
✗ Branch 6 → 26 not taken.
✓ Branch 20 → 8 taken 664 times.
✓ Branch 20 → 21 taken 1084 times.
1748 for (const auto &structManifestations : structs | std::views::values)
306
5/8
✓ Branch 9 → 10 taken 664 times.
✗ Branch 9 → 25 not taken.
✓ Branch 10 → 11 taken 664 times.
✗ Branch 10 → 25 not taken.
✓ Branch 11 → 12 taken 664 times.
✗ Branch 11 → 25 not taken.
✓ Branch 17 → 13 taken 665 times.
✓ Branch 17 → 18 taken 664 times.
1329 for (const auto &manifestation : structManifestations | std::views::values)
307
1/2
✓ Branch 14 → 15 taken 665 times.
✗ Branch 14 → 24 not taken.
665 manifestations.push_back(&manifestation);
308
309 // Sort manifestations by declaration code location
310
2/2
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 334 times.
672 auto sortLambda = [](const Struct *lhs, const Struct *rhs) { return lhs->getDeclCodeLoc() < rhs->getDeclCodeLoc(); };
311
1/2
✓ Branch 21 → 22 taken 1084 times.
✗ Branch 21 → 27 not taken.
1084 std::ranges::sort(manifestations, sortLambda);
312 1084 return manifestations;
313 }
314
315 /**
316 * Check if this struct has any reference fields
317 *
318 * @return Has reference fields or not
319 */
320 352 bool Scope::hasRefFields() {
321
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 352 times.
352 assert(type == ScopeType::STRUCT);
322
2/2
✓ Branch 16 → 5 taken 909 times.
✓ Branch 16 → 17 taken 340 times.
1249 for (size_t i = 0; i < getFieldCount(); i++)
323
3/4
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 909 times.
✓ Branch 12 → 13 taken 12 times.
✓ Branch 12 → 14 taken 897 times.
1818 if (lookupField(i)->getQualType().isRef())
324 12 return true;
325 340 return false;
326 }
327
328 /**
329 * Get the current number of nested loops
330 *
331 * @return Number of loops
332 */
333 2656 unsigned int Scope::getLoopNestingDepth() const { // NOLINT(misc-no-recursion)
334
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 2656 times.
2656 assert(!isRootScope());
335
2/2
✓ Branch 6 → 7 taken 485 times.
✓ Branch 6 → 8 taken 2171 times.
2656 if (parent->parent == nullptr)
336 485 return 0;
337 2171 unsigned int loopCount = parent->getLoopNestingDepth();
338
6/6
✓ Branch 9 → 10 taken 2061 times.
✓ Branch 9 → 12 taken 110 times.
✓ Branch 10 → 11 taken 1331 times.
✓ Branch 10 → 12 taken 730 times.
✓ Branch 11 → 12 taken 24 times.
✓ Branch 11 → 13 taken 1307 times.
2171 if (type == ScopeType::WHILE_BODY || type == ScopeType::FOR_BODY || type == ScopeType::FOREACH_BODY)
339 864 loopCount++;
340 2171 return loopCount;
341 }
342
343 /**
344 * Check if this scope is one of the child scopes of a switch statement
345 *
346 * @return Child scope of switch statement or not
347 */
348 7 bool Scope::isInCaseBranch() const { // NOLINT(misc-no-recursion)
349
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 7 times.
7 assert(!isRootScope());
350
2/2
✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 8 taken 5 times.
7 if (parent->parent == nullptr)
351 2 return false;
352
2/2
✓ Branch 8 → 9 taken 4 times.
✓ Branch 8 → 10 taken 1 time.
5 if (type == ScopeType::CASE_BODY)
353 4 return true;
354 1 return parent->isInCaseBranch();
355 }
356
357 /**
358 * Check if this scope is within an async scope
359 *
360 * @return Within async scope or not
361 */
362 75 bool Scope::isInAsyncScope() const { // NOLINT(misc-no-recursion)
363
2/2
✓ Branch 2 → 3 taken 8 times.
✓ Branch 2 → 4 taken 67 times.
75 if (isAsyncScope)
364 8 return true;
365
3/4
✓ Branch 6 → 7 taken 43 times.
✓ Branch 6 → 10 taken 24 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 43 times.
67 return !isRootScope() && parent->isInAsyncScope();
366 }
367
368 /**
369 * Check if unsafe operations are allowed in this scope
370 *
371 * @return Allowed or not
372 */
373 4959 bool Scope::doesAllowUnsafeOperations() const { // NOLINT(misc-no-recursion)
374
2/2
✓ Branch 2 → 3 taken 3108 times.
✓ Branch 2 → 4 taken 1851 times.
4959 if (type == ScopeType::UNSAFE_BODY)
375 3108 return true;
376
4/4
✓ Branch 6 → 7 taken 1850 times.
✓ Branch 6 → 10 taken 1 time.
✓ Branch 8 → 9 taken 1849 times.
✓ Branch 8 → 10 taken 1 time.
1851 return !isRootScope() && parent->doesAllowUnsafeOperations();
377 }
378
379 /**
380 * Checks if this scope is imported
381 *
382 * @param askingScope Scope, which asks whether the current one is imported from its point of view or not
383 *
384 * @return Imported / not imported
385 */
386 87954 bool Scope::isImportedBy(const Scope *askingScope) const { return askingScope->sourceFile->imports(sourceFile); }
387
388 /**
389 * Get JSON representation of the symbol table
390 *
391 * @return Symbol table as JSON object
392 */
393 74256 nlohmann::json Scope::getSymbolTableJSON() const { // NOLINT(misc-no-recursion)
394
1/2
✓ Branch 2 → 3 taken 74256 times.
✗ Branch 2 → 42 not taken.
74256 nlohmann::json result = symbolTable.toJSON();
395
396 // Collect all children
397 74256 std::vector<nlohmann::json> jsonChildren;
398
1/2
✓ Branch 4 → 5 taken 74256 times.
✗ Branch 4 → 38 not taken.
74256 jsonChildren.reserve(children.size());
399
2/2
✓ Branch 20 → 7 taken 72174 times.
✓ Branch 20 → 21 taken 74256 times.
146430 for (const auto &[name, childScope] : children) {
400
1/2
✓ Branch 11 → 12 taken 72174 times.
✗ Branch 11 → 33 not taken.
72174 nlohmann::json c = childScope->getSymbolTableJSON();
401
2/4
✓ Branch 12 → 13 taken 72174 times.
✗ Branch 12 → 30 not taken.
✓ Branch 13 → 14 taken 72174 times.
✗ Branch 13 → 28 not taken.
72174 c["name"] = name; // Inject symbol table name into JSON object
402
1/2
✓ Branch 16 → 17 taken 72174 times.
✗ Branch 16 → 31 not taken.
72174 jsonChildren.emplace_back(c);
403 72174 }
404
2/4
✓ Branch 21 → 22 taken 74256 times.
✗ Branch 21 → 37 not taken.
✓ Branch 22 → 23 taken 74256 times.
✗ Branch 22 → 35 not taken.
74256 result["children"] = jsonChildren;
405
406 74256 return result;
407 74256 }
408
409 } // namespace spice::compiler
410