GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 98.4% 186 / 4 / 193
Functions: 100.0% 22 / 0 / 22
Branches: 65.2% 257 / 0 / 394

src/symboltablebuilder/Scope.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 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 26287 Scope::Scope(Scope *parent, SourceFile *sourceFile, ScopeType scopeType, const CodeLoc *codeLoc)
13
2/2
✓ Branch 3 → 4 taken 25062 times.
✓ Branch 3 → 5 taken 1225 times.
26287 : parent(parent), sourceFile(sourceFile), codeLoc(codeLoc), type(scopeType) {}
14
15 /**
16 * Create a child scope and return it
17 *
18 * @param scopeName Name of the child scope
19 * @param scopeType Type of the child scope
20 * @param declCodeLoc Code location of the scope
21 * @return Child scope (heap allocated)
22 */
23 25062 Scope *Scope::createChildScope(const std::string &scopeName, ScopeType scopeType, const CodeLoc *declCodeLoc) {
24
3/6
✓ Branch 2 → 3 taken 25062 times.
✗ Branch 2 → 14 not taken.
✓ Branch 3 → 4 taken 25062 times.
✗ Branch 3 → 12 not taken.
✓ Branch 4 → 5 taken 25062 times.
✗ Branch 4 → 10 not taken.
25062 children.insert({scopeName, std::make_shared<Scope>(this, sourceFile, scopeType, declCodeLoc)});
25 25062 return children.at(scopeName).get();
26 }
27
28 /**
29 * Rename the child scope. This is useful for realizing function overloading by storing a function with not
30 * only its name, but also its signature
31 *
32 * @param oldName Old name of the child table
33 * @param newName New name of the child table
34 */
35 13510 void Scope::renameChildScope(const std::string &oldName, const std::string &newName) {
36
4/8
✓ Branch 2 → 3 taken 13510 times.
✗ Branch 2 → 19 not taken.
✓ Branch 3 → 4 taken 13510 times.
✗ Branch 3 → 7 not taken.
✓ Branch 4 → 5 taken 13510 times.
✗ Branch 4 → 19 not taken.
✓ Branch 5 → 6 taken 13510 times.
✗ Branch 5 → 7 not taken.
13510 assert(children.contains(oldName) && !children.contains(newName));
37
1/2
✓ Branch 8 → 9 taken 13510 times.
✗ Branch 8 → 19 not taken.
13510 auto nodeHandler = children.extract(oldName);
38
1/2
✓ Branch 10 → 11 taken 13510 times.
✗ Branch 10 → 17 not taken.
13510 nodeHandler.key() = newName;
39
1/2
✓ Branch 12 → 13 taken 13510 times.
✗ Branch 12 → 16 not taken.
13510 children.insert(std::move(nodeHandler));
40 13510 }
41
42 /**
43 * Duplicates the child scope by copying it. The duplicated symbols point to the original ones.
44 *
45 * @param oldName Old name of the child block
46 * @param newName New block name
47 */
48 4604 Scope *Scope::copyChildScope(const std::string &oldName, const std::string &newName) {
49
4/8
✓ Branch 2 → 3 taken 4604 times.
✗ Branch 2 → 23 not taken.
✓ Branch 3 → 4 taken 4604 times.
✗ Branch 3 → 7 not taken.
✓ Branch 4 → 5 taken 4604 times.
✗ Branch 4 → 23 not taken.
✓ Branch 5 → 6 taken 4604 times.
✗ Branch 5 → 7 not taken.
4604 assert(children.contains(oldName) && !children.contains(newName));
50 // Create copy
51
2/4
✓ Branch 8 → 9 taken 4604 times.
✗ Branch 8 → 23 not taken.
✓ Branch 10 → 11 taken 4604 times.
✗ Branch 10 → 23 not taken.
4604 const std::shared_ptr<Scope> newScope = children.at(oldName)->deepCopyScope();
52 // Save copy under new name
53
2/4
✓ Branch 11 → 12 taken 4604 times.
✗ Branch 11 → 20 not taken.
✓ Branch 12 → 13 taken 4604 times.
✗ Branch 12 → 18 not taken.
4604 children.insert({newName, newScope});
54 9208 return newScope.get();
55 4604 }
56
57 /**
58 * Deep copy the current scope and all its children
59 *
60 * @return Deep copy of the current scope
61 */
62 14630 std::shared_ptr<Scope> Scope::deepCopyScope() { // NOLINT(misc-no-recursion)
63 14630 const auto newScope = std::make_shared<Scope>(*this);
64
2/2
✓ Branch 30 → 5 taken 10026 times.
✓ Branch 30 → 31 taken 14630 times.
24656 for (const auto &[childName, oldChild] : children) {
65
2/4
✓ Branch 9 → 10 taken 10026 times.
✗ Branch 9 → 37 not taken.
✓ Branch 11 → 12 taken 10026 times.
✗ Branch 11 → 35 not taken.
10026 newScope->children[childName] = oldChild->deepCopyScope();
66
1/2
✓ Branch 16 → 17 taken 10026 times.
✗ Branch 16 → 38 not taken.
10026 newScope->children[childName]->parent = newScope.get();
67
2/4
✓ Branch 19 → 20 taken 10026 times.
✗ Branch 19 → 38 not taken.
✓ Branch 22 → 23 taken 10026 times.
✗ Branch 22 → 38 not taken.
10026 newScope->children[childName]->symbolTable.scope = newScope->children[childName].get();
68
1/2
✓ Branch 26 → 27 taken 10026 times.
✗ Branch 26 → 38 not taken.
10026 newScope->children[childName]->symbolTable.parent = &newScope->symbolTable;
69 }
70 14630 newScope->symbolTable.scope = newScope.get();
71 14630 return newScope;
72 } // LCOV_EXCL_LINE - false positive
73
74 /**
75 * Get a child scope of the current scope by its name
76 *
77 * @param scopeName Child scope name
78 * @return Child scope
79 */
80 49884 Scope *Scope::getChildScope(const std::string &scopeName) const {
81
5/6
✓ Branch 3 → 4 taken 49883 times.
✓ Branch 3 → 7 taken 1 time.
✓ Branch 5 → 6 taken 49883 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 49883 times.
✓ Branch 8 → 11 taken 1 time.
49884 if (!children.empty() && children.contains(scopeName))
82 49883 return children.at(scopeName).get();
83 1 return nullptr;
84 }
85
86 /**
87 * Retrieve all variables in the current scope, that have reached the end of their lifetime at the end of this scope
88 *
89 * @return Collection of EOL variables
90 */
91 21780 std::vector<SymbolTableEntry *> Scope::getVarsGoingOutOfScope() { // NOLINT(misc-no-recursion)
92
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 21780 times.
21780 assert(!isRootScope()); // Should not be called in root scope
93 21780 std::vector<SymbolTableEntry *> varsGoingOutOfScope;
94
95 // Collect all variables in this scope
96
2/2
✓ Branch 26 → 8 taken 38892 times.
✓ Branch 26 → 27 taken 21780 times.
60672 for (const auto &[name, entry] : symbolTable.symbols) {
97 // Skip 'this' and result variables
98
8/10
✓ Branch 11 → 12 taken 38892 times.
✗ Branch 11 → 52 not taken.
✓ Branch 12 → 13 taken 32323 times.
✓ Branch 12 → 15 taken 6569 times.
✓ Branch 13 → 14 taken 32323 times.
✗ Branch 13 → 52 not taken.
✓ Branch 14 → 15 taken 8147 times.
✓ Branch 14 → 16 taken 24176 times.
✓ Branch 17 → 18 taken 14716 times.
✓ Branch 17 → 19 taken 24176 times.
38892 if (name == THIS_VARIABLE_NAME || name == RETURN_VARIABLE_NAME)
99 14716 continue;
100 // Skip parameters (ToDo: Remove when copy constructors work for by-value argument passing)
101
2/2
✓ Branch 19 → 20 taken 15541 times.
✓ Branch 19 → 21 taken 8635 times.
24176 if (entry.isParam)
102 15541 continue;
103 // Found variable, that goes out of scope
104
2/4
✓ Branch 21 → 22 taken 8635 times.
✗ Branch 21 → 51 not taken.
✓ Branch 22 → 23 taken 8635 times.
✗ Branch 22 → 51 not taken.
8635 varsGoingOutOfScope.push_back(&symbolTable.symbols.at(name));
105 }
106
107 // If this is the scope of a dtor, also return all fields of the struct
108
2/2
✓ Branch 27 → 28 taken 118 times.
✓ Branch 27 → 49 taken 21662 times.
21780 if (isDtorScope) {
109
2/4
✓ Branch 30 → 31 taken 118 times.
✗ Branch 30 → 33 not taken.
✓ Branch 31 → 32 taken 118 times.
✗ Branch 31 → 33 not taken.
118 assert(!isRootScope() && parent->type == ScopeType::STRUCT);
110 // Get all fields of the struct
111
2/2
✓ Branch 47 → 36 taken 4052 times.
✓ Branch 47 → 48 taken 118 times.
4170 for (const auto &[name, entry] : parent->symbolTable.symbols)
112
4/6
✓ Branch 39 → 40 taken 4052 times.
✗ Branch 39 → 55 not taken.
✓ Branch 40 → 41 taken 4052 times.
✗ Branch 40 → 53 not taken.
✓ Branch 41 → 42 taken 320 times.
✓ Branch 41 → 45 taken 3732 times.
4052 if (!entry.getQualType().isOneOf({TY_FUNCTION, TY_PROCEDURE}))
113
2/4
✓ Branch 42 → 43 taken 320 times.
✗ Branch 42 → 54 not taken.
✓ Branch 43 → 44 taken 320 times.
✗ Branch 43 → 54 not taken.
320 varsGoingOutOfScope.push_back(&parent->symbolTable.symbols.at(name));
114 }
115
116 21780 return varsGoingOutOfScope;
117 } // LCOV_EXCL_LINE - false positive
118
119 /**
120 * Insert a new generic type in this scope
121 *
122 * @param typeName Generic type name
123 * @param genericType Generic type itself
124 */
125 5223 void Scope::insertGenericType(const std::string &typeName, const GenericType &genericType) {
126
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 5223 times.
5223 assert(!genericTypes.contains(typeName));
127
2/4
✓ Branch 5 → 6 taken 5223 times.
✗ Branch 5 → 11 not taken.
✓ Branch 6 → 7 taken 5223 times.
✗ Branch 6 → 9 not taken.
5223 genericTypes.insert({typeName, genericType});
128 5223 }
129
130 /**
131 * Search for a generic type by its name in this scope.
132 * If the generic type is not found, a nullptr is returned.
133 *
134 * @param typeName Name of the generic type
135 * @return Generic type
136 */
137 39180 GenericType *Scope::lookupGenericTypeStrict(const std::string &typeName) {
138
2/2
✓ Branch 3 → 4 taken 16925 times.
✓ Branch 3 → 6 taken 22255 times.
39180 return genericTypes.contains(typeName) ? &genericTypes.at(typeName) : nullptr;
139 }
140
141 /**
142 * Collect all warnings, produced within this scope
143 *
144 * @param warnings List of warnings
145 * @return Collection of warnings
146 */
147 2277 void Scope::collectWarnings(std::vector<CompilerWarning> &warnings) const { // NOLINT(misc-no-recursion)
148 // Visit own symbols
149
5/8
✓ Branch 2 → 3 taken 2277 times.
✗ Branch 2 → 215 not taken.
✓ Branch 3 → 4 taken 2277 times.
✗ Branch 3 → 215 not taken.
✓ Branch 4 → 5 taken 2277 times.
✗ Branch 4 → 215 not taken.
✓ Branch 143 → 6 taken 7111 times.
✓ Branch 143 → 144 taken 2277 times.
9388 for (const SymbolTableEntry &entry : symbolTable.symbols | std::views::values) {
150 // Do not produce a warning if the symbol is used or has a special name
151 7111 const std::string &name = entry.name;
152
6/6
✓ Branch 7 → 8 taken 255 times.
✓ Branch 7 → 10 taken 6856 times.
✓ Branch 9 → 10 taken 100 times.
✓ Branch 9 → 11 taken 155 times.
✓ Branch 12 → 13 taken 6956 times.
✓ Branch 12 → 14 taken 155 times.
7111 if (entry.used || name.starts_with(UNUSED_VARIABLE_NAME))
153 6982 continue;
154
155
1/2
✓ Branch 14 → 15 taken 155 times.
✗ Branch 14 → 214 not taken.
155 const QualType entryType = entry.getQualType();
156
157 // Determine warning type and message by the scope type and the symbol type
158 155 CompilerWarningType warningType = UNUSED_VARIABLE;
159 155 std::string warningMessage;
160
4/4
✓ Branch 16 → 17 taken 24 times.
✓ Branch 16 → 92 taken 48 times.
✓ Branch 16 → 121 taken 27 times.
✓ Branch 16 → 127 taken 56 times.
155 switch (type) {
161 24 case ScopeType::GLOBAL: {
162 // Skip generic function/procedure/struct/interface entries
163
6/10
✓ Branch 17 → 18 taken 24 times.
✗ Branch 17 → 157 not taken.
✓ Branch 18 → 19 taken 14 times.
✓ Branch 18 → 23 taken 10 times.
✓ Branch 19 → 20 taken 14 times.
✗ Branch 19 → 157 not taken.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 14 times.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 24 times.
24 if (entryType.isOneOf({TY_FUNCTION, TY_PROCEDURE, TY_STRUCT, TY_INTERFACE}) && !entryType.getTemplateTypes().empty())
164 continue;
165
166
3/4
✓ Branch 26 → 27 taken 24 times.
✗ Branch 26 → 212 not taken.
✓ Branch 27 → 28 taken 11 times.
✓ Branch 27 → 38 taken 13 times.
24 if (entryType.is(TY_FUNCTION)) {
167
1/2
✓ Branch 28 → 29 taken 11 times.
✗ Branch 28 → 212 not taken.
11 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
168 11 warningType = UNUSED_FUNCTION;
169
3/6
✓ Branch 30 → 31 taken 11 times.
✗ Branch 30 → 162 not taken.
✓ Branch 31 → 32 taken 11 times.
✗ Branch 31 → 160 not taken.
✓ Branch 32 → 33 taken 11 times.
✗ Branch 32 → 158 not taken.
11 warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused";
170
3/4
✓ Branch 38 → 39 taken 13 times.
✗ Branch 38 → 212 not taken.
✓ Branch 39 → 40 taken 1 time.
✓ Branch 39 → 50 taken 12 times.
13 } else if (entryType.is(TY_PROCEDURE)) {
171
1/2
✓ Branch 40 → 41 taken 1 time.
✗ Branch 40 → 212 not taken.
1 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
172 1 warningType = UNUSED_PROCEDURE;
173
3/6
✓ Branch 42 → 43 taken 1 time.
✗ Branch 42 → 169 not taken.
✓ Branch 43 → 44 taken 1 time.
✗ Branch 43 → 167 not taken.
✓ Branch 44 → 45 taken 1 time.
✗ Branch 44 → 165 not taken.
1 warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused";
174
3/4
✓ Branch 50 → 51 taken 12 times.
✗ Branch 50 → 212 not taken.
✓ Branch 51 → 52 taken 1 time.
✓ Branch 51 → 58 taken 11 times.
12 } else if (entryType.is(TY_STRUCT)) {
175 1 warningType = UNUSED_STRUCT;
176
2/4
✓ Branch 52 → 53 taken 1 time.
✗ Branch 52 → 174 not taken.
✓ Branch 53 → 54 taken 1 time.
✗ Branch 53 → 172 not taken.
1 warningMessage = "The struct '" + entry.name + "' is unused";
177
3/4
✓ Branch 58 → 59 taken 11 times.
✗ Branch 58 → 212 not taken.
✓ Branch 59 → 60 taken 1 time.
✓ Branch 59 → 66 taken 10 times.
11 } else if (entryType.is(TY_INTERFACE)) {
178 1 warningType = UNUSED_INTERFACE;
179
2/4
✓ Branch 60 → 61 taken 1 time.
✗ Branch 60 → 178 not taken.
✓ Branch 61 → 62 taken 1 time.
✗ Branch 61 → 176 not taken.
1 warningMessage = "The interface '" + entry.name + "' is unused";
180
3/4
✓ Branch 66 → 67 taken 10 times.
✗ Branch 66 → 212 not taken.
✓ Branch 67 → 68 taken 2 times.
✓ Branch 67 → 69 taken 8 times.
10 } else if (entryType.is(TY_ENUM)) {
181 2 continue; // Do not report unused enums. Only unused enum items are reported
182
3/4
✓ Branch 69 → 70 taken 8 times.
✗ Branch 69 → 212 not taken.
✓ Branch 70 → 71 taken 2 times.
✓ Branch 70 → 77 taken 6 times.
8 } else if (entryType.is(TY_IMPORT)) {
183 2 warningType = UNUSED_IMPORT;
184
2/4
✓ Branch 71 → 72 taken 2 times.
✗ Branch 71 → 182 not taken.
✓ Branch 72 → 73 taken 2 times.
✗ Branch 72 → 180 not taken.
2 warningMessage = "The import '" + entry.name + "' is unused";
185
3/4
✓ Branch 77 → 78 taken 6 times.
✗ Branch 77 → 212 not taken.
✓ Branch 78 → 79 taken 1 time.
✓ Branch 78 → 85 taken 5 times.
6 } else if (entryType.is(TY_ALIAS)) {
186 1 warningType = UNUSED_ALIAS;
187
2/4
✓ Branch 79 → 80 taken 1 time.
✗ Branch 79 → 186 not taken.
✓ Branch 80 → 81 taken 1 time.
✗ Branch 80 → 184 not taken.
1 warningMessage = "The type alias '" + entry.name + "' is unused";
188 } else {
189 5 warningType = UNUSED_VARIABLE;
190
2/4
✓ Branch 85 → 86 taken 5 times.
✗ Branch 85 → 190 not taken.
✓ Branch 86 → 87 taken 5 times.
✗ Branch 86 → 188 not taken.
5 warningMessage = "The variable '" + entry.name + "' is unused";
191 }
192
193 22 break;
194 }
195 48 case ScopeType::STRUCT: // fall-through
196 case ScopeType::INTERFACE: {
197
3/4
✓ Branch 92 → 93 taken 48 times.
✗ Branch 92 → 212 not taken.
✓ Branch 93 → 94 taken 5 times.
✓ Branch 93 → 100 taken 43 times.
48 if (entry.isField()) {
198 5 warningType = UNUSED_FIELD;
199
2/4
✓ Branch 94 → 95 taken 5 times.
✗ Branch 94 → 194 not taken.
✓ Branch 95 → 96 taken 5 times.
✗ Branch 95 → 192 not taken.
5 warningMessage = "The field '" + entry.name + "' is unused";
200
2/4
✓ Branch 100 → 101 taken 43 times.
✗ Branch 100 → 196 not taken.
✓ Branch 101 → 102 taken 43 times.
✗ Branch 101 → 120 not taken.
43 } else if (entryType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) {
201 // Skip implicit method entries and generic templates
202
1/2
✓ Branch 102 → 103 taken 43 times.
✗ Branch 102 → 212 not taken.
43 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
203
5/6
✓ Branch 104 → 105 taken 19 times.
✓ Branch 104 → 107 taken 24 times.
✗ Branch 106 → 107 not taken.
✓ Branch 106 → 108 taken 19 times.
✓ Branch 109 → 110 taken 24 times.
✓ Branch 109 → 111 taken 19 times.
43 if (fctManifestations->empty() || fctManifestations->front()->implicitDefault)
204 24 continue;
205
206 19 warningType = UNUSED_METHOD;
207
3/6
✓ Branch 112 → 113 taken 19 times.
✗ Branch 112 → 201 not taken.
✓ Branch 113 → 114 taken 19 times.
✗ Branch 113 → 199 not taken.
✓ Branch 114 → 115 taken 19 times.
✗ Branch 114 → 197 not taken.
19 warningMessage = "The method '" + fctManifestations->front()->getSignature() + "' is unused";
208 }
209 24 break;
210 }
211 27 case ScopeType::ENUM: {
212 27 warningType = UNUSED_ENUM_ITEM;
213
2/4
✓ Branch 121 → 122 taken 27 times.
✗ Branch 121 → 206 not taken.
✓ Branch 122 → 123 taken 27 times.
✗ Branch 122 → 204 not taken.
27 warningMessage = "The enum item '" + entry.name + "' is unused";
214 27 break;
215 }
216 56 default: {
217 56 warningType = UNUSED_VARIABLE;
218
2/4
✓ Branch 127 → 128 taken 56 times.
✗ Branch 127 → 210 not taken.
✓ Branch 128 → 129 taken 56 times.
✗ Branch 128 → 208 not taken.
56 warningMessage = "The variable '" + entry.name + "' is unused";
219 56 break;
220 }
221 }
222
223 // Add warning
224
2/4
✓ Branch 133 → 134 taken 129 times.
✗ Branch 133 → 212 not taken.
✓ Branch 134 → 135 taken 129 times.
✗ Branch 134 → 212 not taken.
129 warnings.emplace_back(entry.getDeclCodeLoc(), warningType, warningMessage);
225
2/2
✓ Branch 137 → 138 taken 129 times.
✓ Branch 137 → 140 taken 26 times.
155 }
226
227 // Visit children
228
5/8
✓ Branch 144 → 145 taken 2277 times.
✗ Branch 144 → 216 not taken.
✓ Branch 145 → 146 taken 2277 times.
✗ Branch 145 → 216 not taken.
✓ Branch 146 → 147 taken 2277 times.
✗ Branch 146 → 216 not taken.
✓ Branch 155 → 148 taken 2095 times.
✓ Branch 155 → 156 taken 2277 times.
4372 for (const auto &childScope : children | std::views::values)
229
2/2
✓ Branch 150 → 151 taken 1987 times.
✓ Branch 150 → 153 taken 108 times.
2095 if (!childScope->isGenericScope)
230
1/2
✓ Branch 152 → 153 taken 1987 times.
✗ Branch 152 → 216 not taken.
1987 childScope->collectWarnings(warnings);
231 2277 }
232
233 /**
234 * Checks if all variables of this and all child scopes are of an explicit type.
235 * This is executed after type inference to check that all variables could be inferred correctly.
236 */
237 80716 void Scope::ensureSuccessfulTypeInference() const { // NOLINT(misc-no-recursion)
238 // Check symbols in this scope
239
2/2
✓ Branch 19 → 4 taken 181908 times.
✓ Branch 19 → 20 taken 80715 times.
262623 for (auto &[name, entry] : symbolTable.symbols)
240
4/6
✓ Branch 7 → 8 taken 181908 times.
✗ Branch 7 → 40 not taken.
✓ Branch 8 → 9 taken 181908 times.
✗ Branch 8 → 40 not taken.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 17 taken 181907 times.
181908 if (entry.getQualType().is(TY_DYN))
241
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");
242
243 // Check child scopes
244
5/8
✓ Branch 20 → 21 taken 80715 times.
✗ Branch 20 → 41 not taken.
✓ Branch 21 → 22 taken 80715 times.
✗ Branch 21 → 41 not taken.
✓ Branch 22 → 23 taken 80715 times.
✗ Branch 22 → 41 not taken.
✓ Branch 29 → 24 taken 78463 times.
✓ Branch 29 → 30 taken 80714 times.
159177 for (const auto &scope : children | std::views::values)
245
2/2
✓ Branch 26 → 27 taken 78462 times.
✓ Branch 26 → 41 taken 1 time.
78463 scope->ensureSuccessfulTypeInference();
246 80714 }
247
248 /**
249 * Get the number of fields if this is a struct scope
250 *
251 * @return Number of fields
252 */
253 49680 size_t Scope::getFieldCount() const {
254
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 49680 times.
49680 assert(type == ScopeType::STRUCT);
255 49680 size_t fieldCount = 0;
256
5/8
✓ Branch 4 → 5 taken 49680 times.
✗ Branch 4 → 29 not taken.
✓ Branch 5 → 6 taken 49680 times.
✗ Branch 5 → 29 not taken.
✓ Branch 6 → 7 taken 49680 times.
✗ Branch 6 → 29 not taken.
✓ Branch 26 → 8 taken 913854 times.
✓ Branch 26 → 27 taken 49680 times.
963534 for (const auto &symbol : symbolTable.symbols | std::views::values) {
257
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 913854 times.
913854 if (symbol.anonymous)
258 continue;
259
1/2
✓ Branch 11 → 12 taken 913854 times.
✗ Branch 11 → 29 not taken.
913854 const QualType &symbolType = symbol.getQualType();
260
2/4
✓ Branch 12 → 13 taken 913854 times.
✗ Branch 12 → 29 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 913854 times.
913854 if (symbolType.is(TY_IMPORT))
261 continue;
262 913854 const ASTNode *declNode = symbol.declNode;
263
8/10
✓ Branch 15 → 16 taken 913854 times.
✗ Branch 15 → 29 not taken.
✓ Branch 16 → 17 taken 189862 times.
✓ Branch 16 → 19 taken 723992 times.
✓ Branch 17 → 18 taken 189862 times.
✗ Branch 17 → 29 not taken.
✓ Branch 18 → 19 taken 31487 times.
✓ Branch 18 → 20 taken 158375 times.
✓ Branch 21 → 22 taken 755479 times.
✓ Branch 21 → 23 taken 158375 times.
913854 if (declNode->isFctOrProcDef() || declNode->isStructDef())
264 755479 continue;
265 158375 fieldCount++;
266 }
267 49680 return fieldCount;
268 }
269
270 /**
271 * Get all virtual methods in this scope, sorted by declaration code location
272 *
273 * @return List of virtual method pointers
274 */
275 792 std::vector<Function *> Scope::getVirtualMethods() {
276
3/4
✓ Branch 2 → 3 taken 358 times.
✓ Branch 2 → 5 taken 434 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 358 times.
792 assert(type == ScopeType::STRUCT || type == ScopeType::INTERFACE);
277
278 // Collect all virtual methods
279 792 std::vector<Function *> methods;
280
2/2
✓ Branch 37 → 7 taken 5080 times.
✓ Branch 37 → 38 taken 792 times.
5872 for (auto &[fctId, manifestationList] : functions) {
281
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 5080 times.
5080 assert(!manifestationList.empty());
282
2/2
✓ Branch 34 → 15 taken 7376 times.
✓ Branch 34 → 35 taken 5080 times.
12456 for (auto &[mangledName, function] : manifestationList)
283
2/2
✓ Branch 27 → 28 taken 1774 times.
✓ Branch 27 → 32 taken 5602 times.
7376 if (function.isVirtualMethod())
284
3/6
✓ Branch 28 → 29 taken 1774 times.
✗ Branch 28 → 41 not taken.
✓ Branch 29 → 30 taken 1774 times.
✗ Branch 29 → 41 not taken.
✓ Branch 30 → 31 taken 1774 times.
✗ Branch 30 → 41 not taken.
1774 methods.push_back(&functions.at(fctId).at(mangledName));
285 }
286
287 // Sort the list
288
2/4
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 2164 times.
✓ Branch 38 → 39 taken 792 times.
✗ Branch 38 → 44 not taken.
5120 std::ranges::sort(methods, [](const Function *a, const Function *b) { return a->getDeclCodeLoc() < b->getDeclCodeLoc(); });
289
290 792 return methods;
291 } // LCOV_EXCL_LINE - false positive
292
293 /**
294 * Retrieve all struct manifestations in this scope in the order of their declaration
295 *
296 * @return All struct manifestations in declaration order
297 */
298 1185 std::vector<const Struct *> Scope::getAllStructManifestationsInDeclarationOrder() const {
299 // Retrieve all struct manifestations in this scope
300 1185 std::vector<const Struct *> manifestations;
301
1/2
✓ Branch 3 → 4 taken 1185 times.
✗ Branch 3 → 27 not taken.
1185 manifestations.reserve(structs.size()); // Reserve at least the size of individual generic structs
302
5/8
✓ Branch 4 → 5 taken 1185 times.
✗ Branch 4 → 26 not taken.
✓ Branch 5 → 6 taken 1185 times.
✗ Branch 5 → 26 not taken.
✓ Branch 6 → 7 taken 1185 times.
✗ Branch 6 → 26 not taken.
✓ Branch 20 → 8 taken 727 times.
✓ Branch 20 → 21 taken 1185 times.
1912 for (const auto &structManifestations : structs | std::views::values)
303
5/8
✓ Branch 9 → 10 taken 727 times.
✗ Branch 9 → 25 not taken.
✓ Branch 10 → 11 taken 727 times.
✗ Branch 10 → 25 not taken.
✓ Branch 11 → 12 taken 727 times.
✗ Branch 11 → 25 not taken.
✓ Branch 17 → 13 taken 730 times.
✓ Branch 17 → 18 taken 727 times.
1457 for (const auto &manifestation : structManifestations | std::views::values)
304
1/2
✓ Branch 14 → 15 taken 730 times.
✗ Branch 14 → 24 not taken.
730 manifestations.push_back(&manifestation);
305
306 // Sort manifestations by declaration code location
307
2/2
✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 6 taken 384 times.
776 auto sortLambda = [](const Struct *lhs, const Struct *rhs) { return lhs->getDeclCodeLoc() < rhs->getDeclCodeLoc(); };
308
1/2
✓ Branch 21 → 22 taken 1185 times.
✗ Branch 21 → 27 not taken.
1185 std::ranges::sort(manifestations, sortLambda);
309 1185 return manifestations;
310 } // LCOV_EXCL_LINE - false positive
311
312 /**
313 * Get the current number of nested loops
314 *
315 * @return Number of loops
316 */
317 2941 unsigned int Scope::getLoopNestingDepth() const { // NOLINT(misc-no-recursion)
318
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 2941 times.
2941 assert(!isRootScope());
319
2/2
✓ Branch 6 → 7 taken 541 times.
✓ Branch 6 → 8 taken 2400 times.
2941 if (parent->parent == nullptr)
320 541 return 0;
321 2400 unsigned int loopCount = parent->getLoopNestingDepth();
322
6/6
✓ Branch 9 → 10 taken 2278 times.
✓ Branch 9 → 12 taken 122 times.
✓ Branch 10 → 11 taken 1468 times.
✓ Branch 10 → 12 taken 810 times.
✓ Branch 11 → 12 taken 24 times.
✓ Branch 11 → 13 taken 1444 times.
2400 if (type == ScopeType::WHILE_BODY || type == ScopeType::FOR_BODY || type == ScopeType::FOREACH_BODY)
323 956 loopCount++;
324 2400 return loopCount;
325 }
326
327 /**
328 * Check if this scope is one of the child scopes of a switch statement
329 *
330 * @return Child scope of switch statement or not
331 */
332 7 bool Scope::isInCaseBranch() const { // NOLINT(misc-no-recursion)
333
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 7 times.
7 assert(!isRootScope());
334
2/2
✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 8 taken 5 times.
7 if (parent->parent == nullptr)
335 2 return false;
336
2/2
✓ Branch 8 → 9 taken 4 times.
✓ Branch 8 → 10 taken 1 time.
5 if (type == ScopeType::CASE_BODY)
337 4 return true;
338 1 return parent->isInCaseBranch();
339 }
340
341 /**
342 * Check if this scope is within an async scope
343 *
344 * @return Within async scope or not
345 */
346 75 bool Scope::isInAsyncScope() const { // NOLINT(misc-no-recursion)
347
2/2
✓ Branch 2 → 3 taken 8 times.
✓ Branch 2 → 4 taken 67 times.
75 if (isAsyncScope)
348 8 return true;
349
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();
350 }
351
352 /**
353 * Check if unsafe operations are allowed in this scope
354 *
355 * @return Allowed or not
356 */
357 5473 bool Scope::doesAllowUnsafeOperations() const { // NOLINT(misc-no-recursion)
358
2/2
✓ Branch 2 → 3 taken 3433 times.
✓ Branch 2 → 4 taken 2040 times.
5473 if (type == ScopeType::UNSAFE_BODY)
359 3433 return true;
360
4/4
✓ Branch 6 → 7 taken 2039 times.
✓ Branch 6 → 10 taken 1 time.
✓ Branch 8 → 9 taken 2038 times.
✓ Branch 8 → 10 taken 1 time.
2040 return !isRootScope() && parent->doesAllowUnsafeOperations();
361 }
362
363 /**
364 * Checks if this scope is imported
365 *
366 * @param askingScope Scope, which asks whether the current one is imported from its point of view or not
367 *
368 * @return Imported / not imported
369 */
370 101696 bool Scope::isImportedBy(const Scope *askingScope) const { return askingScope->sourceFile->imports(sourceFile); }
371
372 /**
373 * Get JSON representation of the symbol table
374 *
375 * @return Symbol table as JSON object
376 */
377 80780 nlohmann::json Scope::getSymbolTableJSON() const { // NOLINT(misc-no-recursion)
378
1/2
✓ Branch 2 → 3 taken 80780 times.
✗ Branch 2 → 42 not taken.
80780 nlohmann::json result = symbolTable.toJSON();
379
380 // Collect all children
381 80780 std::vector<nlohmann::json> jsonChildren;
382
1/2
✓ Branch 4 → 5 taken 80780 times.
✗ Branch 4 → 38 not taken.
80780 jsonChildren.reserve(children.size());
383
2/2
✓ Branch 20 → 7 taken 78519 times.
✓ Branch 20 → 21 taken 80780 times.
159299 for (const auto &[name, childScope] : children) {
384
1/2
✓ Branch 11 → 12 taken 78519 times.
✗ Branch 11 → 33 not taken.
78519 nlohmann::json c = childScope->getSymbolTableJSON();
385
2/4
✓ Branch 12 → 13 taken 78519 times.
✗ Branch 12 → 30 not taken.
✓ Branch 13 → 14 taken 78519 times.
✗ Branch 13 → 28 not taken.
78519 c["name"] = name; // Inject symbol table name into JSON object
386
1/2
✓ Branch 16 → 17 taken 78519 times.
✗ Branch 16 → 31 not taken.
78519 jsonChildren.emplace_back(c);
387 78519 }
388
2/4
✓ Branch 21 → 22 taken 80780 times.
✗ Branch 21 → 37 not taken.
✓ Branch 22 → 23 taken 80780 times.
✗ Branch 22 → 35 not taken.
80780 result["children"] = jsonChildren;
389
390 80780 return result;
391 80780 }
392
393 } // namespace spice::compiler
394