GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 93.4% 197 / 5 / 216
Functions: 92.0% 23 / 0 / 25
Branches: 61.6% 276 / 0 / 448

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 180895 Scope::Scope(Scope *parent, SourceFile *sourceFile, ScopeType scopeType, const CodeLoc *codeLoc)
13
2/2
✓ Branch 3 → 4 taken 174808 times.
✓ Branch 3 → 5 taken 6087 times.
180895 : 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 174808 Scope *Scope::createChildScope(const std::string &scopeName, ScopeType scopeType, const CodeLoc *declCodeLoc) {
24
2/4
✓ Branch 2 → 3 taken 174808 times.
✗ Branch 2 → 15 not taken.
✓ Branch 3 → 4 taken 174808 times.
✗ Branch 3 → 13 not taken.
174808 const auto &[scope, inserted] = children.emplace(scopeName, std::make_shared<Scope>(this, sourceFile, scopeType, declCodeLoc));
25
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 174808 times.
174808 assert(inserted);
26 349616 return scope->second.get();
27 }
28
29 /**
30 * Rename the child scope. This is useful for realizing function overloading by storing a function with not
31 * only its name, but also its signature
32 *
33 * @param oldName Old name of the child table
34 * @param newName New name of the child table
35 */
36 86823 void Scope::renameChildScope(const std::string &oldName, const std::string &newName) {
37
4/8
✓ Branch 2 → 3 taken 86823 times.
✗ Branch 2 → 20 not taken.
✓ Branch 3 → 4 taken 86823 times.
✗ Branch 3 → 7 not taken.
✓ Branch 4 → 5 taken 86823 times.
✗ Branch 4 → 20 not taken.
✓ Branch 5 → 6 taken 86823 times.
✗ Branch 5 → 7 not taken.
86823 assert(children.contains(oldName) && !children.contains(newName));
38
1/2
✓ Branch 8 → 9 taken 86823 times.
✗ Branch 8 → 20 not taken.
86823 auto nodeHandler = children.extract(oldName);
39
1/2
✓ Branch 10 → 11 taken 86823 times.
✗ Branch 10 → 18 not taken.
86823 nodeHandler.key() = newName;
40
1/2
✓ Branch 13 → 14 taken 86823 times.
✗ Branch 13 → 17 not taken.
173646 children.insert(std::move(nodeHandler));
41 86823 }
42
43 /**
44 * Duplicates the child scope by copying it. The duplicated symbols point to the original ones.
45 *
46 * @param oldName Old name of the child block
47 * @param newName New block name
48 */
49 38641 Scope *Scope::copyChildScope(const std::string &oldName, const std::string &newName) {
50
4/8
✓ Branch 2 → 3 taken 38641 times.
✗ Branch 2 → 18 not taken.
✓ Branch 3 → 4 taken 38641 times.
✗ Branch 3 → 7 not taken.
✓ Branch 4 → 5 taken 38641 times.
✗ Branch 4 → 18 not taken.
✓ Branch 5 → 6 taken 38641 times.
✗ Branch 5 → 7 not taken.
38641 assert(children.contains(oldName) && !children.contains(newName));
51 // Create copy
52
2/4
✓ Branch 8 → 9 taken 38641 times.
✗ Branch 8 → 18 not taken.
✓ Branch 10 → 11 taken 38641 times.
✗ Branch 10 → 18 not taken.
38641 const std::shared_ptr<Scope> newScope = children.at(oldName)->deepCopyScope();
53 // Save copy under new name
54
1/2
✓ Branch 11 → 12 taken 38641 times.
✗ Branch 11 → 16 not taken.
38641 children.emplace(newName, newScope);
55 77282 return newScope.get();
56 38641 }
57
58 /**
59 * Deep copy the current scope and all its children
60 *
61 * @return Deep copy of the current scope
62 */
63 156353 std::shared_ptr<Scope> Scope::deepCopyScope() { // NOLINT(misc-no-recursion)
64 156353 const auto newScope = std::make_shared<Scope>(*this);
65
2/2
✓ Branch 30 → 5 taken 117712 times.
✓ Branch 30 → 31 taken 156353 times.
274065 for (const auto &[childName, oldChild] : children) {
66
2/4
✓ Branch 9 → 10 taken 117712 times.
✗ Branch 9 → 37 not taken.
✓ Branch 11 → 12 taken 117712 times.
✗ Branch 11 → 35 not taken.
117712 newScope->children[childName] = oldChild->deepCopyScope();
67
1/2
✓ Branch 16 → 17 taken 117712 times.
✗ Branch 16 → 38 not taken.
117712 newScope->children[childName]->parent = newScope.get();
68
2/4
✓ Branch 19 → 20 taken 117712 times.
✗ Branch 19 → 38 not taken.
✓ Branch 22 → 23 taken 117712 times.
✗ Branch 22 → 38 not taken.
117712 newScope->children[childName]->symbolTable.scope = newScope->children[childName].get();
69
1/2
✓ Branch 26 → 27 taken 117712 times.
✗ Branch 26 → 38 not taken.
117712 newScope->children[childName]->symbolTable.parent = &newScope->symbolTable;
70 }
71 156353 newScope->symbolTable.scope = newScope.get();
72 156353 return newScope;
73 } // LCOV_EXCL_LINE - false positive
74
75 /**
76 * Get a child scope of the current scope by its name
77 *
78 * @param scopeName Child scope name
79 * @return Child scope
80 */
81 407931 Scope *Scope::getChildScope(const std::string &scopeName) const {
82
1/2
✓ Branch 2 → 3 taken 407931 times.
✗ Branch 2 → 12 not taken.
407931 const auto it = children.find(scopeName);
83
2/2
✓ Branch 5 → 6 taken 407929 times.
✓ Branch 5 → 8 taken 2 times.
815862 return it != children.end() ? it->second.get() : 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 168017 std::vector<SymbolTableEntry *> Scope::getVarsGoingOutOfScope() { // NOLINT(misc-no-recursion)
92
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 168017 times.
168017 assert(!isRootScope()); // Should not be called in root scope
93 168017 std::vector<SymbolTableEntry *> varsGoingOutOfScope;
94
95 // Collect all variables in this scope
96
2/2
✓ Branch 26 → 8 taken 273623 times.
✓ Branch 26 → 27 taken 168017 times.
441640 for (const auto &[name, entry] : symbolTable.symbols) {
97 // Skip 'this' and result variables
98
8/10
✓ Branch 11 → 12 taken 273623 times.
✗ Branch 11 → 52 not taken.
✓ Branch 12 → 13 taken 215943 times.
✓ Branch 12 → 15 taken 57680 times.
✓ Branch 13 → 14 taken 215943 times.
✗ Branch 13 → 52 not taken.
✓ Branch 14 → 15 taken 54043 times.
✓ Branch 14 → 16 taken 161900 times.
✓ Branch 17 → 18 taken 111723 times.
✓ Branch 17 → 19 taken 161900 times.
273623 if (name == THIS_VARIABLE_NAME || name == RETURN_VARIABLE_NAME)
99 111723 continue;
100 // Skip parameters (ToDo: Remove when copy constructors work for by-value argument passing)
101
2/2
✓ Branch 19 → 20 taken 96597 times.
✓ Branch 19 → 21 taken 65303 times.
161900 if (entry.isParam)
102 96597 continue;
103 // Found variable, that goes out of scope
104
2/4
✓ Branch 21 → 22 taken 65303 times.
✗ Branch 21 → 51 not taken.
✓ Branch 22 → 23 taken 65303 times.
✗ Branch 22 → 51 not taken.
65303 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 1382 times.
✓ Branch 27 → 49 taken 166635 times.
168017 if (isDtorScope) {
109
2/4
✓ Branch 30 → 31 taken 1382 times.
✗ Branch 30 → 33 not taken.
✓ Branch 31 → 32 taken 1382 times.
✗ Branch 31 → 33 not taken.
1382 assert(!isRootScope() && parent->type == ScopeType::STRUCT);
110 // Get all fields of the struct
111
2/2
✓ Branch 47 → 36 taken 42127 times.
✓ Branch 47 → 48 taken 1382 times.
43509 for (const auto &[name, entry] : parent->symbolTable.symbols)
112
4/6
✓ Branch 39 → 40 taken 42127 times.
✗ Branch 39 → 55 not taken.
✓ Branch 40 → 41 taken 42127 times.
✗ Branch 40 → 53 not taken.
✓ Branch 41 → 42 taken 4422 times.
✓ Branch 41 → 45 taken 37705 times.
42127 if (!entry.getQualType().isOneOf({TY_FUNCTION, TY_PROCEDURE}))
113
2/4
✓ Branch 42 → 43 taken 4422 times.
✗ Branch 42 → 54 not taken.
✓ Branch 43 → 44 taken 4422 times.
✗ Branch 43 → 54 not taken.
4422 varsGoingOutOfScope.push_back(&parent->symbolTable.symbols.at(name));
114 }
115
116 168017 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 37265 void Scope::insertGenericType(const std::string &typeName, const GenericType &genericType) {
126
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 37265 times.
37265 assert(!genericTypes.contains(typeName));
127 37265 genericTypes.emplace(typeName, genericType);
128 37265 }
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 289269 GenericType *Scope::lookupGenericTypeStrict(const std::string &typeName) {
138
1/2
✓ Branch 2 → 3 taken 289269 times.
✗ Branch 2 → 12 not taken.
289269 const auto it = genericTypes.find(typeName);
139
2/2
✓ Branch 5 → 6 taken 96651 times.
✓ Branch 5 → 8 taken 192618 times.
578538 return it != genericTypes.end() ? &it->second : nullptr;
140 }
141
142 /**
143 * Collect all warnings, produced within this scope
144 *
145 * @param warnings List of warnings
146 * @return Collection of warnings
147 */
148 5956 void Scope::collectWarnings(std::vector<CompilerWarning> &warnings) const { // NOLINT(misc-no-recursion)
149 // Visit own symbols
150
5/8
✓ Branch 2 → 3 taken 5956 times.
✗ Branch 2 → 239 not taken.
✓ Branch 3 → 4 taken 5956 times.
✗ Branch 3 → 239 not taken.
✓ Branch 4 → 5 taken 5956 times.
✗ Branch 4 → 239 not taken.
✓ Branch 163 → 6 taken 21271 times.
✓ Branch 163 → 164 taken 5956 times.
27227 for (const SymbolTableEntry &entry : symbolTable.symbols | std::views::values) {
151 // Do not produce a warning if the symbol is used or has a special name
152 21271 const std::string &name = entry.name;
153
6/6
✓ Branch 7 → 8 taken 683 times.
✓ Branch 7 → 10 taken 20588 times.
✓ Branch 9 → 10 taken 242 times.
✓ Branch 9 → 11 taken 441 times.
✓ Branch 12 → 13 taken 20830 times.
✓ Branch 12 → 14 taken 441 times.
21271 if (entry.used || name.starts_with(UNUSED_VARIABLE_NAME))
154 20938 continue;
155
156
1/2
✓ Branch 14 → 15 taken 441 times.
✗ Branch 14 → 238 not taken.
441 const QualType entryType = entry.getQualType();
157
158 // When compiling a static or shared library, publicly accessible symbols form part of the library's exported
159 // API. They may legitimately go unused within the library itself, since external consumers are expected to
160 // use them, so do not report them as unused. isPublic() is only defined for the type categories checked here
161 // (see the assertion in QualType::isPublic()), so guard the call accordingly.
162
12/18
✓ Branch 15 → 16 taken 441 times.
✗ Branch 15 → 238 not taken.
✓ Branch 16 → 17 taken 172 times.
✓ Branch 16 → 19 taken 269 times.
✓ Branch 17 → 18 taken 172 times.
✗ Branch 17 → 238 not taken.
✓ Branch 18 → 19 taken 4 times.
✓ Branch 18 → 24 taken 168 times.
✓ Branch 19 → 20 taken 273 times.
✗ Branch 19 → 238 not taken.
✓ Branch 20 → 21 taken 119 times.
✓ Branch 20 → 24 taken 154 times.
✓ Branch 21 → 22 taken 119 times.
✗ Branch 21 → 238 not taken.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 119 times.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 441 times.
441 if ((entryType.isExtendedPrimitive() || entryType.is(TY_ENUM)) && entryType.isPublic() && sourceFile->isLibraryOutput())
163 continue;
164
165 // Determine warning type and message by the scope type and the symbol type
166 441 CompilerWarningType warningType = UNUSED_VARIABLE;
167 441 std::string warningMessage;
168
4/4
✓ Branch 28 → 29 taken 34 times.
✓ Branch 28 → 112 taken 194 times.
✓ Branch 28 → 141 taken 6 times.
✓ Branch 28 → 147 taken 207 times.
441 switch (type) {
169 34 case ScopeType::GLOBAL: {
170 // Skip generic function/procedure/struct/interface/union entries
171
4/6
✓ Branch 29 → 30 taken 34 times.
✗ Branch 29 → 177 not taken.
✓ Branch 30 → 31 taken 20 times.
✓ Branch 30 → 35 taken 14 times.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 34 times.
54 if (entryType.isOneOf({TY_FUNCTION, TY_PROCEDURE, TY_STRUCT, TY_INTERFACE, TY_UNION}) &&
172
2/4
✓ Branch 31 → 32 taken 20 times.
✗ Branch 31 → 177 not taken.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 20 times.
20 !entryType.getTemplateTypes().empty())
173 continue;
174
175
3/4
✓ Branch 38 → 39 taken 34 times.
✗ Branch 38 → 236 not taken.
✓ Branch 39 → 40 taken 10 times.
✓ Branch 39 → 50 taken 24 times.
34 if (entryType.is(TY_FUNCTION)) {
176
1/2
✓ Branch 40 → 41 taken 10 times.
✗ Branch 40 → 236 not taken.
10 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
177 10 warningType = UNUSED_FUNCTION;
178
3/6
✓ Branch 42 → 43 taken 10 times.
✗ Branch 42 → 182 not taken.
✓ Branch 43 → 44 taken 10 times.
✗ Branch 43 → 180 not taken.
✓ Branch 44 → 45 taken 10 times.
✗ Branch 44 → 178 not taken.
10 warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused";
179
3/4
✓ Branch 50 → 51 taken 24 times.
✗ Branch 50 → 236 not taken.
✓ Branch 51 → 52 taken 4 times.
✓ Branch 51 → 62 taken 20 times.
24 } else if (entryType.is(TY_PROCEDURE)) {
180
1/2
✓ Branch 52 → 53 taken 4 times.
✗ Branch 52 → 236 not taken.
4 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
181 4 warningType = UNUSED_PROCEDURE;
182
3/6
✓ Branch 54 → 55 taken 4 times.
✗ Branch 54 → 189 not taken.
✓ Branch 55 → 56 taken 4 times.
✗ Branch 55 → 187 not taken.
✓ Branch 56 → 57 taken 4 times.
✗ Branch 56 → 185 not taken.
4 warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused";
183
3/4
✓ Branch 62 → 63 taken 20 times.
✗ Branch 62 → 236 not taken.
✓ Branch 63 → 64 taken 2 times.
✓ Branch 63 → 70 taken 18 times.
20 } else if (entryType.is(TY_STRUCT)) {
184 2 warningType = UNUSED_STRUCT;
185
2/4
✓ Branch 64 → 65 taken 2 times.
✗ Branch 64 → 194 not taken.
✓ Branch 65 → 66 taken 2 times.
✗ Branch 65 → 192 not taken.
2 warningMessage = "The struct '" + entry.name + "' is unused";
186
3/4
✓ Branch 70 → 71 taken 18 times.
✗ Branch 70 → 236 not taken.
✓ Branch 71 → 72 taken 2 times.
✓ Branch 71 → 78 taken 16 times.
18 } else if (entryType.is(TY_INTERFACE)) {
187 2 warningType = UNUSED_INTERFACE;
188
2/4
✓ Branch 72 → 73 taken 2 times.
✗ Branch 72 → 198 not taken.
✓ Branch 73 → 74 taken 2 times.
✗ Branch 73 → 196 not taken.
2 warningMessage = "The interface '" + entry.name + "' is unused";
189
3/4
✓ Branch 78 → 79 taken 16 times.
✗ Branch 78 → 236 not taken.
✓ Branch 79 → 80 taken 2 times.
✓ Branch 79 → 86 taken 14 times.
16 } else if (entryType.is(TY_UNION)) {
190 2 warningType = UNUSED_UNION;
191
2/4
✓ Branch 80 → 81 taken 2 times.
✗ Branch 80 → 202 not taken.
✓ Branch 81 → 82 taken 2 times.
✗ Branch 81 → 200 not taken.
2 warningMessage = "The union '" + entry.name + "' is unused";
192
3/4
✓ Branch 86 → 87 taken 14 times.
✗ Branch 86 → 236 not taken.
✓ Branch 87 → 88 taken 4 times.
✓ Branch 87 → 89 taken 10 times.
14 } else if (entryType.is(TY_ENUM)) {
193 4 continue; // Do not report unused enums. Only unused enum items are reported
194
3/4
✓ Branch 89 → 90 taken 10 times.
✗ Branch 89 → 236 not taken.
✓ Branch 90 → 91 taken 8 times.
✓ Branch 90 → 97 taken 2 times.
10 } else if (entryType.is(TY_IMPORT)) {
195 8 warningType = UNUSED_IMPORT;
196
2/4
✓ Branch 91 → 92 taken 8 times.
✗ Branch 91 → 206 not taken.
✓ Branch 92 → 93 taken 8 times.
✗ Branch 92 → 204 not taken.
8 warningMessage = "The import '" + entry.name + "' is unused";
197
2/4
✓ Branch 97 → 98 taken 2 times.
✗ Branch 97 → 236 not taken.
✓ Branch 98 → 99 taken 2 times.
✗ Branch 98 → 105 not taken.
2 } else if (entryType.is(TY_ALIAS)) {
198 2 warningType = UNUSED_ALIAS;
199
2/4
✓ Branch 99 → 100 taken 2 times.
✗ Branch 99 → 210 not taken.
✓ Branch 100 → 101 taken 2 times.
✗ Branch 100 → 208 not taken.
2 warningMessage = "The type alias '" + entry.name + "' is unused";
200 } else {
201 warningType = UNUSED_VARIABLE;
202 warningMessage = "The variable '" + entry.name + "' is unused";
203 }
204
205 30 break;
206 }
207 194 case ScopeType::STRUCT: // fall-through
208 case ScopeType::INTERFACE: // fall-through
209 case ScopeType::UNION: {
210
3/4
✓ Branch 112 → 113 taken 194 times.
✗ Branch 112 → 236 not taken.
✓ Branch 113 → 114 taken 70 times.
✓ Branch 113 → 120 taken 124 times.
194 if (entry.isField()) {
211 70 warningType = UNUSED_FIELD;
212
2/4
✓ Branch 114 → 115 taken 70 times.
✗ Branch 114 → 218 not taken.
✓ Branch 115 → 116 taken 70 times.
✗ Branch 115 → 216 not taken.
70 warningMessage = "The field '" + entry.name + "' is unused";
213
2/4
✓ Branch 120 → 121 taken 124 times.
✗ Branch 120 → 220 not taken.
✓ Branch 121 → 122 taken 124 times.
✗ Branch 121 → 140 not taken.
124 } else if (entryType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) {
214 // Skip implicit method entries and generic templates
215
1/2
✓ Branch 122 → 123 taken 124 times.
✗ Branch 122 → 236 not taken.
124 const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name);
216
5/6
✓ Branch 124 → 125 taken 20 times.
✓ Branch 124 → 127 taken 104 times.
✗ Branch 126 → 127 not taken.
✓ Branch 126 → 128 taken 20 times.
✓ Branch 129 → 130 taken 104 times.
✓ Branch 129 → 131 taken 20 times.
124 if (fctManifestations->empty() || fctManifestations->front()->implicitDefault)
217 104 continue;
218
219 20 warningType = UNUSED_METHOD;
220
3/6
✓ Branch 132 → 133 taken 20 times.
✗ Branch 132 → 225 not taken.
✓ Branch 133 → 134 taken 20 times.
✗ Branch 133 → 223 not taken.
✓ Branch 134 → 135 taken 20 times.
✗ Branch 134 → 221 not taken.
20 warningMessage = "The method '" + fctManifestations->front()->getSignature() + "' is unused";
221 }
222 90 break;
223 }
224 6 case ScopeType::ENUM: {
225 6 warningType = UNUSED_ENUM_ITEM;
226
2/4
✓ Branch 141 → 142 taken 6 times.
✗ Branch 141 → 230 not taken.
✓ Branch 142 → 143 taken 6 times.
✗ Branch 142 → 228 not taken.
6 warningMessage = "The enum item '" + entry.name + "' is unused";
227 6 break;
228 }
229 207 default: {
230 207 warningType = UNUSED_VARIABLE;
231
2/4
✓ Branch 147 → 148 taken 207 times.
✗ Branch 147 → 234 not taken.
✓ Branch 148 → 149 taken 207 times.
✗ Branch 148 → 232 not taken.
207 warningMessage = "The variable '" + entry.name + "' is unused";
232 207 break;
233 }
234 }
235
236 // Add warning
237
2/4
✓ Branch 153 → 154 taken 333 times.
✗ Branch 153 → 236 not taken.
✓ Branch 154 → 155 taken 333 times.
✗ Branch 154 → 236 not taken.
333 warnings.emplace_back(entry.getDeclCodeLoc(), warningType, warningMessage);
238
2/2
✓ Branch 157 → 158 taken 333 times.
✓ Branch 157 → 160 taken 108 times.
441 }
239
240 // Visit children
241
5/8
✓ Branch 164 → 165 taken 5956 times.
✗ Branch 164 → 240 not taken.
✓ Branch 165 → 166 taken 5956 times.
✗ Branch 165 → 240 not taken.
✓ Branch 166 → 167 taken 5956 times.
✗ Branch 166 → 240 not taken.
✓ Branch 175 → 168 taken 5485 times.
✓ Branch 175 → 176 taken 5956 times.
11441 for (const auto &childScope : children | std::views::values)
242
2/2
✓ Branch 170 → 171 taken 5091 times.
✓ Branch 170 → 173 taken 394 times.
5485 if (!childScope->isGenericScope)
243
1/2
✓ Branch 172 → 173 taken 5091 times.
✗ Branch 172 → 240 not taken.
5091 childScope->collectWarnings(warnings);
244 5956 }
245
246 /**
247 * Checks if all variables of this and all child scopes are of an explicit type.
248 * This is executed after type inference to check that all variables could be inferred correctly.
249 */
250 1127239 void Scope::ensureSuccessfulTypeInference() const { // NOLINT(misc-no-recursion)
251 // Check symbols in this scope
252
2/2
✓ Branch 19 → 4 taken 2357012 times.
✓ Branch 19 → 20 taken 1127237 times.
3484249 for (auto &[name, entry] : symbolTable.symbols)
253
4/6
✓ Branch 7 → 8 taken 2357012 times.
✗ Branch 7 → 40 not taken.
✓ Branch 8 → 9 taken 2357012 times.
✗ Branch 8 → 40 not taken.
✓ Branch 9 → 10 taken 2 times.
✓ Branch 9 → 17 taken 2357010 times.
2357012 if (entry.getQualType().is(TY_DYN))
254
3/6
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 36 not taken.
✓ Branch 12 → 13 taken 2 times.
✗ Branch 12 → 34 not taken.
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 31 not taken.
2 throw SemanticError(entry.declNode, UNEXPECTED_DYN_TYPE, "For the variable '" + name + "' no type could be inferred");
255
256 // Check child scopes
257
5/8
✓ Branch 20 → 21 taken 1127237 times.
✗ Branch 20 → 41 not taken.
✓ Branch 21 → 22 taken 1127237 times.
✗ Branch 21 → 41 not taken.
✓ Branch 22 → 23 taken 1127237 times.
✗ Branch 22 → 41 not taken.
✓ Branch 29 → 24 taken 1110689 times.
✓ Branch 29 → 30 taken 1127235 times.
2237924 for (const auto &scope : children | std::views::values)
258
2/2
✓ Branch 26 → 27 taken 1110687 times.
✓ Branch 26 → 41 taken 2 times.
1110689 scope->ensureSuccessfulTypeInference();
259 1127235 }
260
261 /**
262 * Get the number of fields if this is a struct scope
263 *
264 * @return Number of fields
265 */
266 683448 size_t Scope::getFieldCount() const {
267
3/4
✓ Branch 2 → 3 taken 888 times.
✓ Branch 2 → 5 taken 682560 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 888 times.
683448 assert(type == ScopeType::STRUCT || type == ScopeType::UNION);
268 683448 size_t fieldCount = 0;
269
5/8
✓ Branch 5 → 6 taken 683448 times.
✗ Branch 5 → 32 not taken.
✓ Branch 6 → 7 taken 683448 times.
✗ Branch 6 → 32 not taken.
✓ Branch 7 → 8 taken 683448 times.
✗ Branch 7 → 32 not taken.
✓ Branch 29 → 9 taken 12788889 times.
✓ Branch 29 → 30 taken 683448 times.
13472337 for (const auto &symbol : symbolTable.symbols | std::views::values) {
270
2/2
✓ Branch 10 → 11 taken 4296 times.
✓ Branch 10 → 12 taken 12784593 times.
12788889 if (symbol.anonymous)
271 4296 continue;
272
1/2
✓ Branch 12 → 13 taken 12784593 times.
✗ Branch 12 → 32 not taken.
12784593 const QualType &symbolType = symbol.getQualType();
273
2/4
✓ Branch 13 → 14 taken 12784593 times.
✗ Branch 13 → 32 not taken.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 12784593 times.
12784593 if (symbolType.is(TY_IMPORT))
274 continue;
275 12784593 const ASTNode *declNode = symbol.declNode;
276
10/14
✓ Branch 16 → 17 taken 12784593 times.
✗ Branch 16 → 32 not taken.
✓ Branch 17 → 18 taken 2708222 times.
✓ Branch 17 → 22 taken 10076371 times.
✓ Branch 18 → 19 taken 2708222 times.
✗ Branch 18 → 32 not taken.
✓ Branch 19 → 20 taken 2310671 times.
✓ Branch 19 → 22 taken 397551 times.
✓ Branch 20 → 21 taken 2310671 times.
✗ Branch 20 → 32 not taken.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 2310671 times.
✓ Branch 24 → 25 taken 10473922 times.
✓ Branch 24 → 26 taken 2310671 times.
12784593 if (declNode->isFctOrProcDef() || declNode->isStructDef() || declNode->isUnionDef())
277 10473922 continue;
278 2310671 fieldCount++;
279 }
280 683448 return fieldCount;
281 }
282
283 /**
284 * Get all virtual methods in this scope, sorted by their VTable index
285 *
286 * The VTable index is assigned based on the declaration order of the methods in the implemented interface(s). This must match the
287 * order the call sites use to index into the VTable, which may differ from the order the methods are defined in within the struct.
288 *
289 * @return List of virtual method pointers
290 */
291 9044 std::vector<const Function *> Scope::getVirtualMethods() {
292
3/4
✓ Branch 2 → 3 taken 4520 times.
✓ Branch 2 → 5 taken 4524 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 4520 times.
9044 assert(type == ScopeType::STRUCT || type == ScopeType::INTERFACE);
293
294 // Collect all virtual methods
295 9044 std::vector<const Function *> methods;
296
2/2
✓ Branch 37 → 7 taken 75658 times.
✓ Branch 37 → 38 taken 9044 times.
84702 for (auto &[fctId, manifestationList] : functions) {
297
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 75658 times.
75658 assert(!manifestationList.empty());
298
2/2
✓ Branch 34 → 15 taken 103994 times.
✓ Branch 34 → 35 taken 75658 times.
179652 for (auto &[mangledName, function] : manifestationList)
299
2/2
✓ Branch 27 → 28 taken 29268 times.
✓ Branch 27 → 32 taken 74726 times.
103994 if (function.isVirtualMethod())
300
3/6
✓ Branch 28 → 29 taken 29268 times.
✗ Branch 28 → 41 not taken.
✓ Branch 29 → 30 taken 29268 times.
✗ Branch 29 → 41 not taken.
✓ Branch 30 → 31 taken 29268 times.
✗ Branch 30 → 41 not taken.
29268 methods.push_back(&functions.at(fctId).at(mangledName));
301 }
302
303 // Sort the list by VTable index, so that the VTable layout matches the slot indices used at the call sites
304 84112 const auto pred = [](const Function *a, const Function *b) { return a->vtableIndex < b->vtableIndex; };
305
1/2
✓ Branch 38 → 39 taken 9044 times.
✗ Branch 38 → 44 not taken.
9044 std::ranges::sort(methods, pred);
306
307 9044 return methods;
308 } // LCOV_EXCL_LINE - false positive
309
310 /**
311 * Retrieve all struct manifestations in this scope in the order of their declaration
312 *
313 * @return All struct manifestations in declaration order
314 */
315 5997 std::vector<Struct *> Scope::getAllStructManifestationsInDeclarationOrder() {
316 // Retrieve all struct manifestations in this scope
317 5997 std::vector<Struct *> manifestations;
318
1/2
✓ Branch 3 → 4 taken 5997 times.
✗ Branch 3 → 27 not taken.
5997 manifestations.reserve(structs.size()); // Reserve at least the size of individual generic structs
319
5/8
✓ Branch 4 → 5 taken 5997 times.
✗ Branch 4 → 26 not taken.
✓ Branch 5 → 6 taken 5997 times.
✗ Branch 5 → 26 not taken.
✓ Branch 6 → 7 taken 5997 times.
✗ Branch 6 → 26 not taken.
✓ Branch 20 → 8 taken 7441 times.
✓ Branch 20 → 21 taken 5997 times.
13438 for (auto &structManifestations : structs | std::views::values)
320
5/8
✓ Branch 9 → 10 taken 7441 times.
✗ Branch 9 → 25 not taken.
✓ Branch 10 → 11 taken 7441 times.
✗ Branch 10 → 25 not taken.
✓ Branch 11 → 12 taken 7441 times.
✗ Branch 11 → 25 not taken.
✓ Branch 17 → 13 taken 7453 times.
✓ Branch 17 → 18 taken 7441 times.
14894 for (auto &manifestation : structManifestations | std::views::values)
321
1/2
✓ Branch 14 → 15 taken 7453 times.
✗ Branch 14 → 24 not taken.
7453 manifestations.push_back(&manifestation);
322
323 // Sort manifestations by declaration code location
324
2/2
✓ Branch 4 → 5 taken 20 times.
✓ Branch 4 → 6 taken 21486 times.
43012 auto sortLambda = [](const Struct *lhs, const Struct *rhs) { return lhs->getDeclCodeLoc() < rhs->getDeclCodeLoc(); };
325
1/2
✓ Branch 21 → 22 taken 5997 times.
✗ Branch 21 → 27 not taken.
5997 std::ranges::sort(manifestations, sortLambda);
326 5997 return manifestations;
327 } // LCOV_EXCL_LINE - false positive
328
329 /**
330 * Retrieve all union manifestations in this scope in the order of their declaration
331 *
332 * @return All union manifestations in declaration order
333 */
334 std::vector<Union *> Scope::getAllUnionManifestationsInDeclarationOrder() {
335 // Retrieve all union manifestations in this scope
336 std::vector<Union *> manifestations;
337 manifestations.reserve(unions.size()); // Reserve at least the size of individual generic unions
338 for (auto &unionManifestations : unions | std::views::values)
339 for (auto &manifestation : unionManifestations | std::views::values)
340 manifestations.push_back(&manifestation);
341
342 // Sort manifestations by declaration code location
343 auto sortLambda = [](const Union *lhs, const Union *rhs) { return lhs->getDeclCodeLoc() < rhs->getDeclCodeLoc(); };
344 std::ranges::sort(manifestations, sortLambda);
345 return manifestations;
346 } // LCOV_EXCL_LINE - false positive
347
348 /**
349 * Get the current number of nested loops
350 *
351 * @return Number of loops
352 */
353 14782 unsigned int Scope::getLoopNestingDepth() const { // NOLINT(misc-no-recursion)
354
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 14782 times.
14782 assert(!isRootScope());
355
2/2
✓ Branch 6 → 7 taken 2826 times.
✓ Branch 6 → 8 taken 11956 times.
14782 if (parent->parent == nullptr)
356 2826 return 0;
357 11956 unsigned int loopCount = parent->getLoopNestingDepth();
358
6/6
✓ Branch 9 → 10 taken 10843 times.
✓ Branch 9 → 12 taken 1113 times.
✓ Branch 10 → 11 taken 7707 times.
✓ Branch 10 → 12 taken 3136 times.
✓ Branch 11 → 12 taken 192 times.
✓ Branch 11 → 13 taken 7515 times.
11956 if (type == ScopeType::WHILE_BODY || type == ScopeType::FOR_BODY || type == ScopeType::FOREACH_BODY)
359 4441 loopCount++;
360 11956 return loopCount;
361 }
362
363 /**
364 * Get the nearest enclosing function/procedure/lambda body scope, including this scope itself
365 *
366 * @return Nearest enclosing function/procedure/lambda body scope
367 */
368 108664 Scope *Scope::getFunctionScope() { // NOLINT(misc-no-recursion)
369
4/4
✓ Branch 2 → 3 taken 34335 times.
✓ Branch 2 → 4 taken 74329 times.
✓ Branch 3 → 4 taken 100 times.
✓ Branch 3 → 5 taken 34235 times.
108664 if (type == ScopeType::FUNC_PROC_BODY || type == ScopeType::LAMBDA_BODY)
370 74429 return this;
371
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 34235 times.
34235 assert(!isRootScope());
372 34235 return parent->getFunctionScope();
373 }
374
375 /**
376 * Check if this scope is one of the child scopes of a switch statement
377 *
378 * @return Child scope of switch statement or not
379 */
380 14 bool Scope::isInCaseBranch() const { // NOLINT(misc-no-recursion)
381
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 14 times.
14 assert(!isRootScope());
382
2/2
✓ Branch 6 → 7 taken 4 times.
✓ Branch 6 → 8 taken 10 times.
14 if (parent->parent == nullptr)
383 4 return false;
384
2/2
✓ Branch 8 → 9 taken 8 times.
✓ Branch 8 → 10 taken 2 times.
10 if (type == ScopeType::CASE_BODY)
385 8 return true;
386 2 return parent->isInCaseBranch();
387 }
388
389 /**
390 * Check if this scope is within an async scope
391 *
392 * @return Within async scope or not
393 */
394 454 bool Scope::isInAsyncScope() const { // NOLINT(misc-no-recursion)
395
2/2
✓ Branch 2 → 3 taken 22 times.
✓ Branch 2 → 4 taken 432 times.
454 if (isAsyncScope)
396 22 return true;
397
3/4
✓ Branch 6 → 7 taken 302 times.
✓ Branch 6 → 10 taken 130 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 302 times.
432 return !isRootScope() && parent->isInAsyncScope();
398 }
399
400 /**
401 * Check if unsafe operations are allowed in this scope
402 *
403 * @return Allowed or not
404 */
405 35116 bool Scope::doesAllowUnsafeOperations() const { // NOLINT(misc-no-recursion)
406
2/2
✓ Branch 2 → 3 taken 24043 times.
✓ Branch 2 → 4 taken 11073 times.
35116 if (type == ScopeType::UNSAFE_BODY)
407 24043 return true;
408
4/4
✓ Branch 6 → 7 taken 11009 times.
✓ Branch 6 → 10 taken 64 times.
✓ Branch 8 → 9 taken 10881 times.
✓ Branch 8 → 10 taken 128 times.
11073 return !isRootScope() && parent->doesAllowUnsafeOperations();
409 }
410
411 /**
412 * Checks if this scope is imported
413 *
414 * @param askingScope Scope, which asks whether the current one is imported from its point of view or not
415 *
416 * @return Imported / not imported
417 */
418 759582 bool Scope::isImportedBy(const Scope *askingScope) const { return askingScope->sourceFile->imports(sourceFile); }
419
420 /**
421 * Get JSON representation of the symbol table
422 *
423 * @return Symbol table as JSON object
424 */
425 1127283 nlohmann::json Scope::getSymbolTableJSON() const { // NOLINT(misc-no-recursion)
426
1/2
✓ Branch 2 → 3 taken 1127283 times.
✗ Branch 2 → 42 not taken.
1127283 nlohmann::json result = symbolTable.toJSON();
427
428 // Collect all children
429 1127283 std::vector<nlohmann::json> jsonChildren;
430
1/2
✓ Branch 4 → 5 taken 1127283 times.
✗ Branch 4 → 38 not taken.
1127283 jsonChildren.reserve(children.size());
431
2/2
✓ Branch 20 → 7 taken 1110733 times.
✓ Branch 20 → 21 taken 1127283 times.
2238016 for (const auto &[name, childScope] : children) {
432
1/2
✓ Branch 11 → 12 taken 1110733 times.
✗ Branch 11 → 33 not taken.
1110733 nlohmann::json c = childScope->getSymbolTableJSON();
433
2/4
✓ Branch 12 → 13 taken 1110733 times.
✗ Branch 12 → 30 not taken.
✓ Branch 13 → 14 taken 1110733 times.
✗ Branch 13 → 28 not taken.
1110733 c["name"] = name; // Inject symbol table name into JSON object
434
1/2
✓ Branch 16 → 17 taken 1110733 times.
✗ Branch 16 → 31 not taken.
1110733 jsonChildren.emplace_back(c);
435 1110733 }
436
2/4
✓ Branch 21 → 22 taken 1127283 times.
✗ Branch 21 → 37 not taken.
✓ Branch 22 → 23 taken 1127283 times.
✗ Branch 22 → 35 not taken.
1127283 result["children"] = jsonChildren;
437
438 1127283 return result;
439 1127283 }
440
441 } // namespace spice::compiler
442