GCC Code Coverage Report


Directory: ../
File: src/symboltablebuilder/Scope.cpp
Date: 2025-03-05 01:50:32
Exec Total Coverage
Lines: 187 197 94.9%
Functions: 22 22 100.0%
Branches: 259 403 64.3%

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 20226 Scope *Scope::createChildScope(const std::string &scopeName, ScopeType scopeType, const CodeLoc *declCodeLoc) {
21
3/6
✓ Branch 0 (2→3) taken 20226 times.
✗ Branch 1 (2→14) not taken.
✓ Branch 2 (3→4) taken 20226 times.
✗ Branch 3 (3→12) not taken.
✓ Branch 4 (4→5) taken 20226 times.
✗ Branch 5 (4→10) not taken.
20226 children.insert({scopeName, std::make_shared<Scope>(this, sourceFile, scopeType, declCodeLoc)});
22 20226 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 10479 void Scope::renameChildScope(const std::string &oldName, const std::string &newName) {
33
4/8
✓ Branch 0 (2→3) taken 10479 times.
✗ Branch 1 (2→19) not taken.
✓ Branch 2 (3→4) taken 10479 times.
✗ Branch 3 (3→7) not taken.
✓ Branch 4 (4→5) taken 10479 times.
✗ Branch 5 (4→19) not taken.
✓ Branch 6 (5→6) taken 10479 times.
✗ Branch 7 (5→7) not taken.
10479 assert(children.contains(oldName) && !children.contains(newName));
34
1/2
✓ Branch 0 (8→9) taken 10479 times.
✗ Branch 1 (8→19) not taken.
10479 auto nodeHandler = children.extract(oldName);
35
1/2
✓ Branch 0 (10→11) taken 10479 times.
✗ Branch 1 (10→17) not taken.
10479 nodeHandler.key() = newName;
36
1/2
✓ Branch 0 (12→13) taken 10479 times.
✗ Branch 1 (12→16) not taken.
10479 children.insert(std::move(nodeHandler));
37 10479 }
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 3004 Scope *Scope::copyChildScope(const std::string &oldName, const std::string &newName) {
46
4/8
✓ Branch 0 (2→3) taken 3004 times.
✗ Branch 1 (2→23) not taken.
✓ Branch 2 (3→4) taken 3004 times.
✗ Branch 3 (3→7) not taken.
✓ Branch 4 (4→5) taken 3004 times.
✗ Branch 5 (4→23) not taken.
✓ Branch 6 (5→6) taken 3004 times.
✗ Branch 7 (5→7) not taken.
3004 assert(children.contains(oldName) && !children.contains(newName));
47 // Create copy
48
2/4
✓ Branch 0 (8→9) taken 3004 times.
✗ Branch 1 (8→23) not taken.
✓ Branch 2 (10→11) taken 3004 times.
✗ Branch 3 (10→23) not taken.
3004 const std::shared_ptr<Scope> newScope = children.at(oldName)->deepCopyScope();
49 // Save copy under new name
50
2/4
✓ Branch 0 (11→12) taken 3004 times.
✗ Branch 1 (11→20) not taken.
✓ Branch 2 (12→13) taken 3004 times.
✗ Branch 3 (12→18) not taken.
3004 children.insert({newName, newScope});
51 6008 return newScope.get();
52 3004 }
53
54 /**
55 * Deep copy the current scope and all its children
56 *
57 * @return Deep copy of the current scope
58 */
59 10847 std::shared_ptr<Scope> Scope::deepCopyScope() { // NOLINT(misc-no-recursion)
60 10847 const std::shared_ptr<Scope> newScope = std::make_shared<Scope>(*this);
61
2/2
✓ Branch 0 (30→5) taken 7843 times.
✓ Branch 1 (30→31) taken 10847 times.
18690 for (const auto &[childName, oldChild] : children) {
62
2/4
✓ Branch 0 (9→10) taken 7843 times.
✗ Branch 1 (9→37) not taken.
✓ Branch 2 (11→12) taken 7843 times.
✗ Branch 3 (11→35) not taken.
7843 newScope->children[childName] = oldChild->deepCopyScope();
63
1/2
✓ Branch 0 (16→17) taken 7843 times.
✗ Branch 1 (16→38) not taken.
7843 newScope->children[childName]->parent = newScope.get();
64
2/4
✓ Branch 0 (19→20) taken 7843 times.
✗ Branch 1 (19→38) not taken.
✓ Branch 2 (22→23) taken 7843 times.
✗ Branch 3 (22→38) not taken.
7843 newScope->children[childName]->symbolTable.scope = newScope->children[childName].get();
65
1/2
✓ Branch 0 (26→27) taken 7843 times.
✗ Branch 1 (26→38) not taken.
7843 newScope->children[childName]->symbolTable.parent = &newScope->symbolTable;
66 }
67 10847 newScope->symbolTable.scope = newScope.get();
68 10847 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 40650 Scope *Scope::getChildScope(const std::string &scopeName) const {
78
5/6
✓ Branch 0 (3→4) taken 40649 times.
✓ Branch 1 (3→7) taken 1 times.
✓ Branch 2 (5→6) taken 40649 times.
✗ Branch 3 (5→7) not taken.
✓ Branch 4 (8→9) taken 40649 times.
✓ Branch 5 (8→11) taken 1 times.
40650 if (!children.empty() && children.contains(scopeName))
79 40649 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 17224 std::vector<SymbolTableEntry *> Scope::getVarsGoingOutOfScope() { // NOLINT(misc-no-recursion)
89
1/2
✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 17224 times.
17224 assert(!isRootScope()); // Should not be called in root scope
90 17224 std::vector<SymbolTableEntry *> varsGoingOutOfScope;
91
92 // Collect all variables in this scope
93
2/2
✓ Branch 0 (26→8) taken 29055 times.
✓ Branch 1 (26→27) taken 17224 times.
46279 for (const auto &[name, entry] : symbolTable.symbols) {
94 // Skip 'this' and result variables
95
8/10
✓ Branch 0 (11→12) taken 29055 times.
✗ Branch 1 (11→52) not taken.
✓ Branch 2 (12→13) taken 23520 times.
✓ Branch 3 (12→15) taken 5535 times.
✓ Branch 4 (13→14) taken 23520 times.
✗ Branch 5 (13→52) not taken.
✓ Branch 6 (14→15) taken 6297 times.
✓ Branch 7 (14→16) taken 17223 times.
✓ Branch 8 (17→18) taken 11832 times.
✓ Branch 9 (17→19) taken 17223 times.
29055 if (name == THIS_VARIABLE_NAME || name == RETURN_VARIABLE_NAME)
96 11832 continue;
97 // Skip parameters (ToDo: Remove when copy constructors work for by-value argument passing)
98
2/2
✓ Branch 0 (19→20) taken 10163 times.
✓ Branch 1 (19→21) taken 7060 times.
17223 if (entry.isParam)
99 10163 continue;
100 // Found variable, that goes out of scope
101
2/4
✓ Branch 0 (21→22) taken 7060 times.
✗ Branch 1 (21→51) not taken.
✓ Branch 2 (22→23) taken 7060 times.
✗ Branch 3 (22→51) not taken.
7060 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 (27→28) taken 102 times.
✓ Branch 1 (27→49) taken 17122 times.
17224 if (isDtorScope) {
106
2/4
✓ Branch 0 (30→31) taken 102 times.
✗ Branch 1 (30→33) not taken.
✓ Branch 2 (31→32) taken 102 times.
✗ Branch 3 (31→33) not taken.
102 assert(!isRootScope() && parent->type == ScopeType::STRUCT);
107 // Get all fields of the struct
108
2/2
✓ Branch 0 (47→36) taken 3509 times.
✓ Branch 1 (47→48) taken 102 times.
3611 for (const auto &[name, entry] : parent->symbolTable.symbols)
109
4/6
✓ Branch 0 (39→40) taken 3509 times.
✗ Branch 1 (39→55) not taken.
✓ Branch 2 (40→41) taken 3509 times.
✗ Branch 3 (40→53) not taken.
✓ Branch 4 (41→42) taken 277 times.
✓ Branch 5 (41→45) taken 3232 times.
3509 if (!entry.getQualType().isOneOf({TY_FUNCTION, TY_PROCEDURE}))
110
2/4
✓ Branch 0 (42→43) taken 277 times.
✗ Branch 1 (42→54) not taken.
✓ Branch 2 (43→44) taken 277 times.
✗ Branch 3 (43→54) not taken.
277 varsGoingOutOfScope.push_back(&parent->symbolTable.symbols.at(name));
111 }
112
113 17224 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 2921 void Scope::insertGenericType(const std::string &typeName, const GenericType &genericType) {
123
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 2921 times.
2921 assert(!genericTypes.contains(typeName));
124
2/4
✓ Branch 0 (5→6) taken 2921 times.
✗ Branch 1 (5→11) not taken.
✓ Branch 2 (6→7) taken 2921 times.
✗ Branch 3 (6→9) not taken.
2921 genericTypes.insert({typeName, genericType});
125 2921 }
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 28059 GenericType *Scope::lookupGenericTypeStrict(const std::string &typeName) {
135
2/2
✓ Branch 0 (3→4) taken 10808 times.
✓ Branch 1 (3→6) taken 17251 times.
28059 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 1404 void Scope::collectWarnings(std::vector<CompilerWarning> &warnings) const { // NOLINT(misc-no-recursion)
145 // Visit own symbols
146 CompilerWarningType warningType;
147 1404 std::string warningMessage;
148
5/8
✓ Branch 0 (3→4) taken 1404 times.
✗ Branch 1 (3→213) not taken.
✓ Branch 2 (4→5) taken 1404 times.
✗ Branch 3 (4→213) not taken.
✓ Branch 4 (5→6) taken 1404 times.
✗ Branch 5 (5→213) not taken.
✓ Branch 6 (142→7) taken 2997 times.
✓ Branch 7 (142→143) taken 1404 times.
4401 for (const auto &entry : symbolTable.symbols | std::views::values) {
149 // Do not produce a warning if the symbol is used or has a special name
150 2997 const std::string &name = entry.name;
151
6/6
✓ Branch 0 (8→9) taken 233 times.
✓ Branch 1 (8→11) taken 2764 times.
✓ Branch 2 (10→11) taken 60 times.
✓ Branch 3 (10→12) taken 173 times.
✓ Branch 4 (13→14) taken 2824 times.
✓ Branch 5 (13→15) taken 173 times.
2997 if (entry.used || name.starts_with(UNUSED_VARIABLE_NAME))
152 2848 continue;
153
154
1/2
✓ Branch 0 (15→16) taken 173 times.
✗ Branch 1 (15→212) not taken.
173 const QualType entryType = entry.getQualType();
155
156 // Determine warning type and message by the scope type and the symbol type
157
4/5
✓ Branch 0 (16→17) taken 28 times.
✓ Branch 1 (16→92) taken 52 times.
✓ Branch 2 (16→121) taken 37 times.
✗ Branch 3 (16→127) not taken.
✓ Branch 4 (16→130) taken 56 times.
173 switch (type) {
158 28 case ScopeType::GLOBAL: {
159 // Skip generic function/procedure/struct/interface entries
160
6/10
✓ Branch 0 (17→18) taken 28 times.
✗ Branch 1 (17→157) 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→157) 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())
161 continue;
162
163
3/4
✓ Branch 0 (26→27) taken 28 times.
✗ Branch 1 (26→212) not taken.
✓ Branch 2 (27→28) taken 11 times.
✓ Branch 3 (27→38) taken 17 times.
28 if (entryType.is(TY_FUNCTION)) {
164
1/2
✓ Branch 0 (28→29) taken 11 times.
✗ Branch 1 (28→212) not taken.
11 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
165 11 warningType = UNUSED_FUNCTION;
166
3/6
✓ Branch 0 (30→31) taken 11 times.
✗ Branch 1 (30→162) not taken.
✓ Branch 2 (31→32) taken 11 times.
✗ Branch 3 (31→160) not taken.
✓ Branch 4 (32→33) taken 11 times.
✗ Branch 5 (32→158) not taken.
11 warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused";
167
3/4
✓ Branch 0 (38→39) taken 17 times.
✗ Branch 1 (38→212) not taken.
✓ Branch 2 (39→40) taken 2 times.
✓ Branch 3 (39→50) taken 15 times.
17 } else if (entryType.is(TY_PROCEDURE)) {
168
1/2
✓ Branch 0 (40→41) taken 2 times.
✗ Branch 1 (40→212) not taken.
2 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
169 2 warningType = UNUSED_PROCEDURE;
170
3/6
✓ Branch 0 (42→43) taken 2 times.
✗ Branch 1 (42→169) not taken.
✓ Branch 2 (43→44) taken 2 times.
✗ Branch 3 (43→167) not taken.
✓ Branch 4 (44→45) taken 2 times.
✗ Branch 5 (44→165) not taken.
2 warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused";
171
3/4
✓ Branch 0 (50→51) taken 15 times.
✗ Branch 1 (50→212) not taken.
✓ Branch 2 (51→52) taken 2 times.
✓ Branch 3 (51→58) taken 13 times.
15 } else if (entryType.is(TY_STRUCT)) {
172 2 warningType = UNUSED_STRUCT;
173
2/4
✓ Branch 0 (52→53) taken 2 times.
✗ Branch 1 (52→174) not taken.
✓ Branch 2 (53→54) taken 2 times.
✗ Branch 3 (53→172) not taken.
2 warningMessage = "The struct '" + entry.name + "' is unused";
174
3/4
✓ Branch 0 (58→59) taken 13 times.
✗ Branch 1 (58→212) not taken.
✓ Branch 2 (59→60) taken 1 times.
✓ Branch 3 (59→66) taken 12 times.
13 } else if (entryType.is(TY_INTERFACE)) {
175 1 warningType = UNUSED_INTERFACE;
176
2/4
✓ Branch 0 (60→61) taken 1 times.
✗ Branch 1 (60→178) not taken.
✓ Branch 2 (61→62) taken 1 times.
✗ Branch 3 (61→176) not taken.
1 warningMessage = "The interface '" + entry.name + "' is unused";
177
3/4
✓ Branch 0 (66→67) taken 12 times.
✗ Branch 1 (66→212) not taken.
✓ Branch 2 (67→68) taken 2 times.
✓ Branch 3 (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 0 (69→70) taken 10 times.
✗ Branch 1 (69→212) not taken.
✓ Branch 2 (70→71) taken 4 times.
✓ Branch 3 (70→77) taken 6 times.
10 } else if (entryType.is(TY_IMPORT)) {
180 4 warningType = UNUSED_IMPORT;
181
2/4
✓ Branch 0 (71→72) taken 4 times.
✗ Branch 1 (71→182) not taken.
✓ Branch 2 (72→73) taken 4 times.
✗ Branch 3 (72→180) not taken.
4 warningMessage = "The import '" + entry.name + "' is unused";
182
3/4
✓ Branch 0 (77→78) taken 6 times.
✗ Branch 1 (77→212) not taken.
✓ Branch 2 (78→79) taken 1 times.
✓ Branch 3 (78→85) taken 5 times.
6 } else if (entryType.is(TY_ALIAS)) {
183 1 warningType = UNUSED_ALIAS;
184
2/4
✓ Branch 0 (79→80) taken 1 times.
✗ Branch 1 (79→186) not taken.
✓ Branch 2 (80→81) taken 1 times.
✗ Branch 3 (80→184) not taken.
1 warningMessage = "The type alias '" + entry.name + "' is unused";
185 } else {
186 5 warningType = UNUSED_VARIABLE;
187
2/4
✓ Branch 0 (85→86) taken 5 times.
✗ Branch 1 (85→190) not taken.
✓ Branch 2 (86→87) taken 5 times.
✗ Branch 3 (86→188) not taken.
5 warningMessage = "The variable '" + entry.name + "' is unused";
188 }
189
190 26 break;
191 }
192 52 case ScopeType::STRUCT: // fall-through
193 case ScopeType::INTERFACE: {
194
3/4
✓ Branch 0 (92→93) taken 52 times.
✗ Branch 1 (92→212) not taken.
✓ Branch 2 (93→94) taken 11 times.
✓ Branch 3 (93→100) taken 41 times.
52 if (entry.isField()) {
195 11 warningType = UNUSED_FIELD;
196
2/4
✓ Branch 0 (94→95) taken 11 times.
✗ Branch 1 (94→194) not taken.
✓ Branch 2 (95→96) taken 11 times.
✗ Branch 3 (95→192) not taken.
11 warningMessage = "The field '" + entry.name + "' is unused";
197
2/4
✓ Branch 0 (100→101) taken 41 times.
✗ Branch 1 (100→196) not taken.
✓ Branch 2 (101→102) taken 41 times.
✗ Branch 3 (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 0 (102→103) taken 41 times.
✗ Branch 1 (102→212) not taken.
41 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
200
5/6
✓ Branch 0 (104→105) taken 19 times.
✓ Branch 1 (104→107) taken 22 times.
✗ Branch 2 (106→107) not taken.
✓ Branch 3 (106→108) taken 19 times.
✓ Branch 4 (109→110) taken 22 times.
✓ Branch 5 (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 0 (112→113) taken 19 times.
✗ Branch 1 (112→201) not taken.
✓ Branch 2 (113→114) taken 19 times.
✗ Branch 3 (113→199) not taken.
✓ Branch 4 (114→115) taken 19 times.
✗ Branch 5 (114→197) 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 0 (121→122) taken 37 times.
✗ Branch 1 (121→206) not taken.
✓ Branch 2 (122→123) taken 37 times.
✗ Branch 3 (122→204) 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 56 warningType = UNUSED_VARIABLE;
221
2/4
✓ Branch 0 (130→131) taken 56 times.
✗ Branch 1 (130→210) not taken.
✓ Branch 2 (131→132) taken 56 times.
✗ Branch 3 (131→208) not taken.
56 warningMessage = "The variable '" + entry.name + "' is unused";
222 56 break;
223 }
224 }
225
226 // Add warning
227
2/4
✓ Branch 0 (136→137) taken 149 times.
✗ Branch 1 (136→212) not taken.
✓ Branch 2 (137→138) taken 149 times.
✗ Branch 3 (137→212) not taken.
149 warnings.emplace_back(entry.getDeclCodeLoc(), warningType, warningMessage);
228 }
229
230 // Visit children
231
5/8
✓ Branch 0 (143→144) taken 1404 times.
✗ Branch 1 (143→214) not taken.
✓ Branch 2 (144→145) taken 1404 times.
✗ Branch 3 (144→214) not taken.
✓ Branch 4 (145→146) taken 1404 times.
✗ Branch 5 (145→214) not taken.
✓ Branch 6 (154→147) taken 1181 times.
✓ Branch 7 (154→155) taken 1404 times.
2585 for (const auto &childScope : children | std::views::values)
232
2/2
✓ Branch 0 (149→150) taken 1144 times.
✓ Branch 1 (149→152) taken 37 times.
1181 if (!childScope->isGenericScope)
233
1/2
✓ Branch 0 (151→152) taken 1144 times.
✗ Branch 1 (151→214) not taken.
1144 childScope->collectWarnings(warnings);
234 1404 }
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 59521 void Scope::ensureSuccessfulTypeInference() const { // NOLINT(misc-no-recursion)
241 // Check symbols in this scope
242
2/2
✓ Branch 0 (19→4) taken 132226 times.
✓ Branch 1 (19→20) taken 59520 times.
191746 for (auto &[name, entry] : symbolTable.symbols)
243
4/6
✓ Branch 0 (7→8) taken 132226 times.
✗ Branch 1 (7→40) not taken.
✓ Branch 2 (8→9) taken 132226 times.
✗ Branch 3 (8→40) not taken.
✓ Branch 4 (9→10) taken 1 times.
✓ Branch 5 (9→17) taken 132225 times.
132226 if (entry.getQualType().is(TY_DYN))
244
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");
245
246 // Check child scopes
247
5/8
✓ Branch 0 (20→21) taken 59520 times.
✗ Branch 1 (20→41) not taken.
✓ Branch 2 (21→22) taken 59520 times.
✗ Branch 3 (21→41) not taken.
✓ Branch 4 (22→23) taken 59520 times.
✗ Branch 5 (22→41) not taken.
✓ Branch 6 (29→24) taken 57757 times.
✓ Branch 7 (29→30) taken 59519 times.
117276 for (const auto &scope : children | std::views::values)
248
2/2
✓ Branch 0 (26→27) taken 57756 times.
✓ Branch 1 (26→41) taken 1 times.
57757 scope->ensureSuccessfulTypeInference();
249 59519 }
250
251 /**
252 * Get the number of fields if this is a struct scope
253 *
254 * @return Number of fields
255 */
256 39423 size_t Scope::getFieldCount() const {
257
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 39423 times.
39423 assert(type == ScopeType::STRUCT);
258 39423 size_t fieldCount = 0;
259
5/8
✓ Branch 0 (4→5) taken 39423 times.
✗ Branch 1 (4→29) not taken.
✓ Branch 2 (5→6) taken 39423 times.
✗ Branch 3 (5→29) not taken.
✓ Branch 4 (6→7) taken 39423 times.
✗ Branch 5 (6→29) not taken.
✓ Branch 6 (26→8) taken 753150 times.
✓ Branch 7 (26→27) taken 39423 times.
792573 for (const auto &symbol : symbolTable.symbols | std::views::values) {
260
1/2
✗ Branch 0 (9→10) not taken.
✓ Branch 1 (9→11) taken 753150 times.
753150 if (symbol.anonymous)
261 continue;
262
1/2
✓ Branch 0 (11→12) taken 753150 times.
✗ Branch 1 (11→29) not taken.
753150 const QualType &symbolType = symbol.getQualType();
263
2/4
✓ Branch 0 (12→13) taken 753150 times.
✗ Branch 1 (12→29) not taken.
✗ Branch 2 (13→14) not taken.
✓ Branch 3 (13→15) taken 753150 times.
753150 if (symbolType.is(TY_IMPORT))
264 continue;
265 753150 const ASTNode *declNode = symbol.declNode;
266
8/10
✓ Branch 0 (15→16) taken 753150 times.
✗ Branch 1 (15→29) not taken.
✓ Branch 2 (16→17) taken 153886 times.
✓ Branch 3 (16→19) taken 599264 times.
✓ Branch 4 (17→18) taken 153886 times.
✗ Branch 5 (17→29) not taken.
✓ Branch 6 (18→19) taken 26562 times.
✓ Branch 7 (18→20) taken 127324 times.
✓ Branch 8 (21→22) taken 625826 times.
✓ Branch 9 (21→23) taken 127324 times.
753150 if (declNode->isFctOrProcDef() || declNode->isStructDef())
267 625826 continue;
268 127324 fieldCount++;
269 }
270 39423 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 586 std::vector<Function *> Scope::getVirtualMethods() {
279
3/4
✓ Branch 0 (2→3) taken 256 times.
✓ Branch 1 (2→5) taken 330 times.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 256 times.
586 assert(type == ScopeType::STRUCT || type == ScopeType::INTERFACE);
280
281 // Collect all virtual methods
282 586 std::vector<Function *> methods;
283
2/2
✓ Branch 0 (37→7) taken 3866 times.
✓ Branch 1 (37→38) taken 586 times.
4452 for (auto &[fctId, manifestationList] : functions) {
284
1/2
✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→13) taken 3866 times.
3866 assert(!manifestationList.empty());
285
2/2
✓ Branch 0 (34→15) taken 5606 times.
✓ Branch 1 (34→35) taken 3866 times.
9472 for (auto &[mangledName, function] : manifestationList)
286
2/2
✓ Branch 0 (27→28) taken 1302 times.
✓ Branch 1 (27→32) taken 4304 times.
5606 if (function.isVirtualMethod())
287
3/6
✓ Branch 0 (28→29) taken 1302 times.
✗ Branch 1 (28→41) not taken.
✓ Branch 2 (29→30) taken 1302 times.
✗ Branch 3 (29→41) not taken.
✓ Branch 4 (30→31) taken 1302 times.
✗ Branch 5 (30→41) not taken.
1302 methods.push_back(&functions.at(fctId).at(mangledName));
288 }
289
290 // Sort the list
291
2/4
✓ Branch 0 (38→39) taken 586 times.
✗ Branch 1 (38→44) not taken.
✗ Branch 2 (4→5) not taken.
✓ Branch 3 (4→6) taken 1588 times.
3762 std::ranges::sort(methods, [](const Function *a, const Function *b) { return a->getDeclCodeLoc() < b->getDeclCodeLoc(); });
292
293 586 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 1009 std::vector<const Struct *> Scope::getAllStructManifestationsInDeclarationOrder() const {
302 // Retrieve all struct manifestations in this scope
303 1009 std::vector<const Struct *> manifestations;
304
1/2
✓ Branch 0 (3→4) taken 1009 times.
✗ Branch 1 (3→27) not taken.
1009 manifestations.reserve(structs.size()); // Reserve at least the size of individual generic structs
305
5/8
✓ Branch 0 (4→5) taken 1009 times.
✗ Branch 1 (4→26) not taken.
✓ Branch 2 (5→6) taken 1009 times.
✗ Branch 3 (5→26) not taken.
✓ Branch 4 (6→7) taken 1009 times.
✗ Branch 5 (6→26) not taken.
✓ Branch 6 (20→8) taken 599 times.
✓ Branch 7 (20→21) taken 1009 times.
1608 for (const auto &structManifestations : structs | std::views::values)
306
5/8
✓ Branch 0 (9→10) taken 599 times.
✗ Branch 1 (9→25) not taken.
✓ Branch 2 (10→11) taken 599 times.
✗ Branch 3 (10→25) not taken.
✓ Branch 4 (11→12) taken 599 times.
✗ Branch 5 (11→25) not taken.
✓ Branch 6 (17→13) taken 600 times.
✓ Branch 7 (17→18) taken 599 times.
1199 for (const auto &manifestation : structManifestations | std::views::values)
307
1/2
✓ Branch 0 (14→15) taken 600 times.
✗ Branch 1 (14→24) not taken.
600 manifestations.push_back(&manifestation);
308
309 // Sort manifestations by declaration code location
310
2/2
✓ Branch 0 (4→5) taken 2 times.
✓ Branch 1 (4→6) taken 294 times.
592 auto sortLambda = [](const Struct *lhs, const Struct *rhs) { return lhs->getDeclCodeLoc() < rhs->getDeclCodeLoc(); };
311
1/2
✓ Branch 0 (21→22) taken 1009 times.
✗ Branch 1 (21→27) not taken.
1009 std::ranges::sort(manifestations, sortLambda);
312 1009 return manifestations;
313 }
314
315 /**
316 * Check if this struct has any reference fields
317 *
318 * @return Has reference fields or not
319 */
320 316 bool Scope::hasRefFields() {
321
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 316 times.
316 assert(type == ScopeType::STRUCT);
322
2/2
✓ Branch 0 (16→5) taken 822 times.
✓ Branch 1 (16→17) taken 308 times.
1130 for (size_t i = 0; i < getFieldCount(); i++)
323
3/4
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 822 times.
✓ Branch 2 (12→13) taken 8 times.
✓ Branch 3 (12→14) taken 814 times.
1644 if (lookupField(i)->getQualType().isRef())
324 8 return true;
325 308 return false;
326 }
327
328 /**
329 * Get the current number of nested loops
330 *
331 * @return Number of loops
332 */
333 2540 unsigned int Scope::getLoopNestingDepth() const { // NOLINT(misc-no-recursion)
334
1/2
✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 2540 times.
2540 assert(!isRootScope());
335
2/2
✓ Branch 0 (6→7) taken 464 times.
✓ Branch 1 (6→8) taken 2076 times.
2540 if (parent->parent == nullptr)
336 464 return 0;
337 2076 unsigned int loopCount = parent->getLoopNestingDepth();
338
6/6
✓ Branch 0 (9→10) taken 1971 times.
✓ Branch 1 (9→12) taken 105 times.
✓ Branch 2 (10→11) taken 1273 times.
✓ Branch 3 (10→12) taken 698 times.
✓ Branch 4 (11→12) taken 24 times.
✓ Branch 5 (11→13) taken 1249 times.
2076 if (type == ScopeType::WHILE_BODY || type == ScopeType::FOR_BODY || type == ScopeType::FOREACH_BODY)
339 827 loopCount++;
340 2076 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 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 7 times.
7 assert(!isRootScope());
350
2/2
✓ Branch 0 (6→7) taken 2 times.
✓ Branch 1 (6→8) taken 5 times.
7 if (parent->parent == nullptr)
351 2 return false;
352
2/2
✓ Branch 0 (8→9) taken 4 times.
✓ Branch 1 (8→10) taken 1 times.
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 0 (2→3) taken 8 times.
✓ Branch 1 (2→4) taken 67 times.
75 if (isAsyncScope)
364 8 return true;
365
3/4
✓ Branch 0 (6→7) taken 43 times.
✓ Branch 1 (6→10) taken 24 times.
✗ Branch 2 (8→9) not taken.
✓ Branch 3 (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 4703 bool Scope::doesAllowUnsafeOperations() const { // NOLINT(misc-no-recursion)
374
2/2
✓ Branch 0 (2→3) taken 2943 times.
✓ Branch 1 (2→4) taken 1760 times.
4703 if (type == ScopeType::UNSAFE_BODY)
375 2943 return true;
376
4/4
✓ Branch 0 (6→7) taken 1759 times.
✓ Branch 1 (6→10) taken 1 times.
✓ Branch 2 (8→9) taken 1758 times.
✓ Branch 3 (8→10) taken 1 times.
1760 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 79643 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 59585 nlohmann::json Scope::getSymbolTableJSON() const { // NOLINT(misc-no-recursion)
394
1/2
✓ Branch 0 (2→3) taken 59585 times.
✗ Branch 1 (2→42) not taken.
59585 nlohmann::json result = symbolTable.toJSON();
395
396 // Collect all children
397 59585 std::vector<nlohmann::json> jsonChildren;
398
1/2
✓ Branch 0 (4→5) taken 59585 times.
✗ Branch 1 (4→38) not taken.
59585 jsonChildren.reserve(children.size());
399
2/2
✓ Branch 0 (20→7) taken 57813 times.
✓ Branch 1 (20→21) taken 59585 times.
117398 for (const auto &[name, childScope] : children) {
400
1/2
✓ Branch 0 (11→12) taken 57813 times.
✗ Branch 1 (11→33) not taken.
57813 nlohmann::json c = childScope->getSymbolTableJSON();
401
2/4
✓ Branch 0 (12→13) taken 57813 times.
✗ Branch 1 (12→30) not taken.
✓ Branch 2 (13→14) taken 57813 times.
✗ Branch 3 (13→28) not taken.
57813 c["name"] = name; // Inject symbol table name into JSON object
402
1/2
✓ Branch 0 (16→17) taken 57813 times.
✗ Branch 1 (16→31) not taken.
57813 jsonChildren.emplace_back(c);
403 57813 }
404
2/4
✓ Branch 0 (21→22) taken 59585 times.
✗ Branch 1 (21→37) not taken.
✓ Branch 2 (22→23) taken 59585 times.
✗ Branch 3 (22→35) not taken.
59585 result["children"] = jsonChildren;
405
406 59585 return result;
407 59585 }
408
409 } // namespace spice::compiler
410