Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2021-2024 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 | 15340 | Scope *Scope::createChildScope(const std::string &scopeName, ScopeType scopeType, const CodeLoc *declCodeLoc) { | |
21 |
3/6✓ Branch 1 taken 15340 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15340 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 15340 times.
✗ Branch 8 not taken.
|
15340 | children.insert({scopeName, std::make_shared<Scope>(this, sourceFile, scopeType, declCodeLoc)}); |
22 | 15340 | 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 | 7634 | void Scope::renameChildScope(const std::string &oldName, const std::string &newName) { | |
33 |
4/8✓ Branch 1 taken 7634 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7634 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7634 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7634 times.
✗ Branch 9 not taken.
|
7634 | assert(children.contains(oldName) && !children.contains(newName)); |
34 |
1/2✓ Branch 1 taken 7634 times.
✗ Branch 2 not taken.
|
7634 | auto nodeHandler = children.extract(oldName); |
35 |
1/2✓ Branch 2 taken 7634 times.
✗ Branch 3 not taken.
|
7634 | nodeHandler.key() = newName; |
36 |
1/2✓ Branch 2 taken 7634 times.
✗ Branch 3 not taken.
|
7634 | children.insert(std::move(nodeHandler)); |
37 | 7634 | } | |
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 | 2556 | Scope *Scope::copyChildScope(const std::string &oldName, const std::string &newName) { | |
46 |
4/8✓ Branch 1 taken 2556 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2556 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2556 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2556 times.
✗ Branch 9 not taken.
|
2556 | assert(children.contains(oldName) && !children.contains(newName)); |
47 | // Create copy | ||
48 |
2/4✓ Branch 1 taken 2556 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 2556 times.
✗ Branch 6 not taken.
|
2556 | const std::shared_ptr<Scope> newScope = children.at(oldName)->deepCopyScope(); |
49 | // Save copy under new name | ||
50 |
2/4✓ Branch 1 taken 2556 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2556 times.
✗ Branch 5 not taken.
|
2556 | children.insert({newName, newScope}); |
51 | 5112 | return newScope.get(); | |
52 | 2556 | } | |
53 | |||
54 | /** | ||
55 | * Deep copy the current scope and all its children | ||
56 | * | ||
57 | * @return Deep copy of the current scope | ||
58 | */ | ||
59 | 8999 | std::shared_ptr<Scope> Scope::deepCopyScope() { // NOLINT(misc-no-recursion) | |
60 | 8999 | const std::shared_ptr<Scope> newScope = std::make_shared<Scope>(*this); | |
61 |
2/2✓ Branch 6 taken 6443 times.
✓ Branch 7 taken 8999 times.
|
15442 | for (const auto &[childName, oldChild] : children) { |
62 |
2/4✓ Branch 2 taken 6443 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 6443 times.
✗ Branch 7 not taken.
|
6443 | newScope->children[childName] = oldChild->deepCopyScope(); |
63 |
1/2✓ Branch 3 taken 6443 times.
✗ Branch 4 not taken.
|
6443 | newScope->children[childName]->parent = newScope.get(); |
64 |
2/4✓ Branch 2 taken 6443 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 6443 times.
✗ Branch 8 not taken.
|
6443 | newScope->children[childName]->symbolTable.scope = newScope->children[childName].get(); |
65 |
1/2✓ Branch 3 taken 6443 times.
✗ Branch 4 not taken.
|
6443 | newScope->children[childName]->symbolTable.parent = &newScope->symbolTable; |
66 | } | ||
67 | 8999 | newScope->symbolTable.scope = newScope.get(); | |
68 | 8999 | 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 | 32470 | Scope *Scope::getChildScope(const std::string &scopeName) const { | |
78 |
5/6✓ Branch 1 taken 32469 times.
✓ Branch 2 taken 1 times.
✓ Branch 4 taken 32469 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 32469 times.
✓ Branch 7 taken 1 times.
|
32470 | if (!children.empty() && children.contains(scopeName)) |
79 | 32469 | 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 | 13222 | std::vector<SymbolTableEntry *> Scope::getVarsGoingOutOfScope() { // NOLINT(misc-no-recursion) | |
89 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13222 times.
|
13222 | assert(parent != nullptr); // Should not be called in root scope |
90 | 13222 | std::vector<SymbolTableEntry *> varsGoingOutOfScope; | |
91 | |||
92 | // Collect all variables in this scope | ||
93 |
2/2✓ Branch 7 taken 22958 times.
✓ Branch 8 taken 13222 times.
|
36180 | for (const auto &[name, entry] : symbolTable.symbols) { |
94 | // Skip 'this' and result variables | ||
95 |
8/10✓ Branch 1 taken 22958 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18180 times.
✓ Branch 4 taken 4778 times.
✓ Branch 6 taken 18180 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4405 times.
✓ Branch 9 taken 13775 times.
✓ Branch 10 taken 9183 times.
✓ Branch 11 taken 13775 times.
|
22958 | if (name == THIS_VARIABLE_NAME || name == RETURN_VARIABLE_NAME) |
96 | 9183 | continue; | |
97 | // Skip parameters (ToDo: Remove when copy constructors work for by-value argument passing) | ||
98 |
2/2✓ Branch 0 taken 7344 times.
✓ Branch 1 taken 6431 times.
|
13775 | if (entry.isParam) |
99 | 7344 | continue; | |
100 | // Found variable, that goes out of scope | ||
101 |
2/4✓ Branch 1 taken 6431 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6431 times.
✗ Branch 5 not taken.
|
6431 | 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 taken 88 times.
✓ Branch 1 taken 13134 times.
|
13222 | if (isDtorScope) { |
106 |
2/4✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 88 times.
✗ Branch 3 not taken.
|
88 | assert(parent != nullptr && parent->type == ScopeType::STRUCT); |
107 | // Get all fields of the struct | ||
108 |
2/2✓ Branch 7 taken 2769 times.
✓ Branch 8 taken 88 times.
|
2857 | for (const auto &[name, entry] : parent->symbolTable.symbols) |
109 |
4/6✓ Branch 1 taken 2769 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2769 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 247 times.
✓ Branch 7 taken 2522 times.
|
2769 | if (!entry.getQualType().isOneOf({TY_FUNCTION, TY_PROCEDURE})) |
110 |
2/4✓ Branch 1 taken 247 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 247 times.
✗ Branch 5 not taken.
|
247 | varsGoingOutOfScope.push_back(&parent->symbolTable.symbols.at(name)); |
111 | } | ||
112 | |||
113 | 13222 | 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 | 2489 | void Scope::insertGenericType(const std::string &typeName, const GenericType &genericType) { | |
123 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2489 times.
|
2489 | assert(!genericTypes.contains(typeName)); |
124 |
2/4✓ Branch 1 taken 2489 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2489 times.
✗ Branch 5 not taken.
|
2489 | genericTypes.insert({typeName, genericType}); |
125 | 2489 | } | |
126 | |||
127 | /** | ||
128 | * Search for a generic type by its name. If it was not found, the parent scopes will be searched. | ||
129 | * If the generic type does not exist at all, the function will return a nullptr. | ||
130 | * | ||
131 | * @param typeName Name of the generic type | ||
132 | * @return Generic type | ||
133 | */ | ||
134 | 22375 | GenericType *Scope::lookupGenericType(const std::string &typeName) { // NOLINT(misc-no-recursion) | |
135 |
2/2✓ Branch 1 taken 8383 times.
✓ Branch 2 taken 13992 times.
|
22375 | if (genericTypes.contains(typeName)) |
136 | 8383 | return &genericTypes.at(typeName); | |
137 |
2/2✓ Branch 0 taken 1721 times.
✓ Branch 1 taken 12271 times.
|
13992 | return parent ? parent->lookupGenericType(typeName) : nullptr; |
138 | } | ||
139 | |||
140 | /** | ||
141 | * Collect all warnings, produced within this scope | ||
142 | * | ||
143 | * @param warnings List of warnings | ||
144 | * @return Collection of warnings | ||
145 | */ | ||
146 | 1269 | void Scope::collectWarnings(std::vector<CompilerWarning> &warnings) const { // NOLINT(misc-no-recursion) | |
147 | // Visit own symbols | ||
148 | CompilerWarningType warningType; | ||
149 | 1269 | std::string warningMessage; | |
150 |
5/8✓ Branch 1 taken 1269 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1269 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1269 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 2621 times.
✓ Branch 13 taken 1269 times.
|
3890 | for (const auto &entry : symbolTable.symbols | std::views::values) { |
151 | // Do not produce a warning if the symbol is used or has a special name | ||
152 | 2621 | const std::string &name = entry.name; | |
153 |
6/6✓ Branch 0 taken 166 times.
✓ Branch 1 taken 2455 times.
✓ Branch 3 taken 61 times.
✓ Branch 4 taken 105 times.
✓ Branch 5 taken 2516 times.
✓ Branch 6 taken 105 times.
|
2621 | if (entry.used || name.starts_with(UNUSED_VARIABLE_NAME)) |
154 | 2532 | continue; | |
155 | |||
156 |
1/2✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
|
105 | const QualType entryType = entry.getQualType(); |
157 | |||
158 | // Determine warning type and message by the scope type and the symbol type | ||
159 |
4/5✓ Branch 0 taken 23 times.
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
|
105 | switch (type) { |
160 | 23 | case ScopeType::GLOBAL: { | |
161 | // Skip generic function/procedure/struct/interface entries | ||
162 |
6/10✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 7 times.
✓ Branch 6 taken 16 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 16 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 23 times.
|
23 | if (entryType.isOneOf({TY_FUNCTION, TY_PROCEDURE, TY_STRUCT, TY_INTERFACE}) && !entryType.getTemplateTypes().empty()) |
163 | ✗ | continue; | |
164 | |||
165 |
3/4✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 12 times.
|
23 | if (entryType.is(TY_FUNCTION)) { |
166 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name); |
167 | 11 | warningType = UNUSED_FUNCTION; | |
168 |
3/6✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 11 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 11 times.
✗ Branch 9 not taken.
|
11 | warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused"; |
169 |
3/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 10 times.
|
12 | } else if (entryType.is(TY_PROCEDURE)) { |
170 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name); |
171 | 2 | warningType = UNUSED_PROCEDURE; | |
172 |
3/6✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
|
2 | warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused"; |
173 |
3/4✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 8 times.
|
10 | } else if (entryType.is(TY_STRUCT)) { |
174 | 2 | warningType = UNUSED_STRUCT; | |
175 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | warningMessage = "The struct '" + entry.name + "' is unused"; |
176 |
3/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 7 times.
|
8 | } else if (entryType.is(TY_INTERFACE)) { |
177 | 1 | warningType = UNUSED_INTERFACE; | |
178 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | warningMessage = "The interface '" + entry.name + "' is unused"; |
179 |
3/4✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 5 times.
|
7 | } else if (entryType.is(TY_ENUM)) { |
180 | 2 | continue; // Do not report unused enums. Only unused enum items are reported | |
181 |
3/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 1 times.
|
5 | } else if (entryType.is(TY_IMPORT)) { |
182 | 4 | warningType = UNUSED_IMPORT; | |
183 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
4 | warningMessage = "The import '" + entry.name + "' is unused"; |
184 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1 | } else if (entryType.is(TY_ALIAS)) { |
185 | 1 | warningType = UNUSED_ALIAS; | |
186 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | warningMessage = "The type alias '" + entry.name + "' is unused"; |
187 | } else { | ||
188 | ✗ | warningType = UNUSED_VARIABLE; | |
189 | ✗ | warningMessage = "The variable '" + entry.name + "' is unused"; | |
190 | } | ||
191 | |||
192 | 21 | break; | |
193 | } | ||
194 | 43 | case ScopeType::STRUCT: // fall-through | |
195 | case ScopeType::INTERFACE: { | ||
196 |
3/4✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
✓ Branch 4 taken 10 times.
|
43 | if (entryType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) { |
197 | // Skip implicit method entries and generic templates | ||
198 |
1/2✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
|
33 | const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name); |
199 |
5/6✓ Branch 1 taken 19 times.
✓ Branch 2 taken 14 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
✓ Branch 6 taken 14 times.
✓ Branch 7 taken 19 times.
|
33 | if (fctManifestations->empty() || fctManifestations->front()->implicitDefault) |
200 | 14 | continue; | |
201 | |||
202 | 19 | warningType = UNUSED_METHOD; | |
203 |
3/6✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 19 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 19 times.
✗ Branch 9 not taken.
|
19 | warningMessage = "The method '" + fctManifestations->front()->getSignature() + "' is unused"; |
204 | } else { | ||
205 | 10 | warningType = UNUSED_FIELD; | |
206 |
2/4✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
|
10 | warningMessage = "The field '" + entry.name + "' is unused"; |
207 | } | ||
208 | 29 | break; | |
209 | } | ||
210 | 18 | case ScopeType::ENUM: { | |
211 | 18 | warningType = UNUSED_ENUM_ITEM; | |
212 |
2/4✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
|
18 | warningMessage = "The enum item '" + entry.name + "' is unused"; |
213 | 18 | break; | |
214 | } | ||
215 | ✗ | case ScopeType::FOREACH_BODY: { | |
216 | // Skip idx variables | ||
217 | ✗ | if (entry.name == FOREACH_DEFAULT_IDX_VARIABLE_NAME) | |
218 | ✗ | continue; | |
219 | [[fallthrough]]; | ||
220 | } | ||
221 | default: { | ||
222 | 21 | warningType = UNUSED_VARIABLE; | |
223 |
2/4✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
|
21 | warningMessage = "The variable '" + entry.name + "' is unused"; |
224 | 21 | break; | |
225 | } | ||
226 | } | ||
227 | |||
228 | // Add warning | ||
229 |
2/4✓ Branch 1 taken 89 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 89 times.
✗ Branch 5 not taken.
|
89 | warnings.emplace_back(entry.getDeclCodeLoc(), warningType, warningMessage); |
230 | } | ||
231 | |||
232 | // Visit children | ||
233 |
5/8✓ Branch 1 taken 1269 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1269 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1269 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 1064 times.
✓ Branch 13 taken 1269 times.
|
2333 | for (const auto &childScope : children | std::views::values) |
234 |
2/2✓ Branch 1 taken 1029 times.
✓ Branch 2 taken 35 times.
|
1064 | if (!childScope->isGenericScope) |
235 |
1/2✓ Branch 2 taken 1029 times.
✗ Branch 3 not taken.
|
1029 | childScope->collectWarnings(warnings); |
236 | 1269 | } | |
237 | |||
238 | /** | ||
239 | * Checks if all variables of this and all child scopes are of an explicit type. | ||
240 | * This is executed after type inference to check that all variables could be inferred correctly. | ||
241 | */ | ||
242 | 147051 | void Scope::ensureSuccessfulTypeInference() const { // NOLINT(misc-no-recursion) | |
243 | // Check symbols in this scope | ||
244 |
2/2✓ Branch 7 taken 322945 times.
✓ Branch 8 taken 147050 times.
|
469995 | for (auto &[name, entry] : symbolTable.symbols) |
245 |
4/6✓ Branch 1 taken 322945 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 322945 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 322944 times.
|
322945 | if (entry.getQualType().is(TY_DYN)) |
246 |
3/6✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
|
1 | throw SemanticError(entry.declNode, UNEXPECTED_DYN_TYPE, "For the variable '" + name + "' no type could be inferred"); |
247 | |||
248 | // Check child scopes | ||
249 |
5/8✓ Branch 1 taken 147050 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 147050 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 147050 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 143752 times.
✓ Branch 13 taken 147049 times.
|
290801 | for (const auto &scope : children | std::views::values) |
250 |
2/2✓ Branch 2 taken 143751 times.
✓ Branch 3 taken 1 times.
|
143752 | scope->ensureSuccessfulTypeInference(); |
251 | 147049 | } | |
252 | |||
253 | /** | ||
254 | * Get the number of fields if this is a struct scope | ||
255 | * | ||
256 | * @return Number of fields | ||
257 | */ | ||
258 | 37621 | size_t Scope::getFieldCount() const { | |
259 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 37621 times.
|
37621 | assert(type == ScopeType::STRUCT); |
260 | 37621 | size_t fieldCount = 0; | |
261 |
5/8✓ Branch 1 taken 37621 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 37621 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 37621 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 656707 times.
✓ Branch 13 taken 37621 times.
|
694328 | for (const auto &symbol : symbolTable.symbols | std::views::values) { |
262 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 656707 times.
|
656707 | if (symbol.anonymous) |
263 | ✗ | continue; | |
264 |
1/2✓ Branch 1 taken 656707 times.
✗ Branch 2 not taken.
|
656707 | const QualType &symbolType = symbol.getQualType(); |
265 |
2/4✓ Branch 1 taken 656707 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 656707 times.
|
656707 | if (symbolType.is(TY_IMPORT)) |
266 | ✗ | continue; | |
267 | 656707 | const ASTNode *declNode = symbol.declNode; | |
268 |
8/10✓ Branch 1 taken 656707 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 154720 times.
✓ Branch 4 taken 501987 times.
✓ Branch 6 taken 154720 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 27558 times.
✓ Branch 9 taken 127162 times.
✓ Branch 10 taken 529545 times.
✓ Branch 11 taken 127162 times.
|
656707 | if (declNode->isFctOrProcDef() || declNode->isStructDef()) |
269 | 529545 | continue; | |
270 | 127162 | fieldCount++; | |
271 | } | ||
272 | 37621 | return fieldCount; | |
273 | } | ||
274 | |||
275 | /** | ||
276 | * Get all virtual methods in this scope, sorted by declaration code location | ||
277 | * | ||
278 | * @return List of virtual method pointers | ||
279 | */ | ||
280 | 498 | std::vector<Function *> Scope::getVirtualMethods() { | |
281 |
3/4✓ Branch 0 taken 216 times.
✓ Branch 1 taken 282 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 216 times.
|
498 | assert(type == ScopeType::STRUCT || type == ScopeType::INTERFACE); |
282 | |||
283 | // Collect all virtual methods | ||
284 | 498 | std::vector<Function *> methods; | |
285 |
2/2✓ Branch 7 taken 3254 times.
✓ Branch 8 taken 498 times.
|
3752 | for (auto &[fctId, manifestationList] : functions) { |
286 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3254 times.
|
3254 | assert(!manifestationList.empty()); |
287 |
2/2✓ Branch 7 taken 4774 times.
✓ Branch 8 taken 3254 times.
|
8028 | for (auto &[mangledName, function] : manifestationList) |
288 |
2/2✓ Branch 0 taken 1104 times.
✓ Branch 1 taken 3670 times.
|
4774 | if (function.isVirtualMethod()) |
289 |
3/6✓ Branch 1 taken 1104 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1104 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1104 times.
✗ Branch 8 not taken.
|
1104 | methods.push_back(&functions.at(fctId).at(mangledName)); |
290 | } | ||
291 | |||
292 | // Sort the list | ||
293 |
2/4✓ Branch 1 taken 498 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1360 times.
|
3218 | std::ranges::sort(methods, [](const Function *a, const Function *b) { return a->getDeclCodeLoc() < b->getDeclCodeLoc(); }); |
294 | |||
295 | 498 | return methods; | |
296 | ✗ | } | |
297 | |||
298 | /** | ||
299 | * Retrieve all struct manifestations in this scope in the order of their declaration | ||
300 | * | ||
301 | * @return All struct manifestations in declaration order | ||
302 | */ | ||
303 | 854 | std::vector<const Struct *> Scope::getAllStructManifestationsInDeclarationOrder() const { | |
304 | // Retrieve all struct manifestations in this scope | ||
305 | 854 | std::vector<const Struct *> manifestations; | |
306 |
1/2✓ Branch 2 taken 854 times.
✗ Branch 3 not taken.
|
854 | manifestations.reserve(structs.size()); // Reserve at least the size of individual generic structs |
307 |
5/8✓ Branch 1 taken 854 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 854 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 854 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 538 times.
✓ Branch 13 taken 854 times.
|
1392 | for (const auto &structManifestations : structs | std::views::values) |
308 |
5/8✓ Branch 1 taken 538 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 538 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 538 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 539 times.
✓ Branch 12 taken 538 times.
|
1077 | for (const auto &manifestation : structManifestations | std::views::values) |
309 |
1/2✓ Branch 1 taken 539 times.
✗ Branch 2 not taken.
|
539 | manifestations.push_back(&manifestation); |
310 | |||
311 | // Sort manifestations by declaration code location | ||
312 |
2/2✓ Branch 2 taken 2 times.
✓ Branch 3 taken 208 times.
|
420 | auto sortLambda = [](const Struct *lhs, const Struct *rhs) { return lhs->getDeclCodeLoc() < rhs->getDeclCodeLoc(); }; |
313 |
1/2✓ Branch 1 taken 854 times.
✗ Branch 2 not taken.
|
854 | std::ranges::sort(manifestations, sortLambda); |
314 | 1708 | return manifestations; | |
315 | ✗ | } | |
316 | |||
317 | /** | ||
318 | * Check if this struct has any reference fields | ||
319 | * | ||
320 | * @return Has reference fields or not | ||
321 | */ | ||
322 | 183 | bool Scope::hasRefFields() { | |
323 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 183 times.
|
183 | assert(type == ScopeType::STRUCT); |
324 |
2/2✓ Branch 1 taken 452 times.
✓ Branch 2 taken 176 times.
|
628 | for (size_t i = 0; i < getFieldCount(); i++) |
325 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 452 times.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 445 times.
|
904 | if (lookupField(i)->getQualType().isRef()) |
326 | 7 | return true; | |
327 | 176 | return false; | |
328 | } | ||
329 | |||
330 | /** | ||
331 | * Get the current number of nested loops | ||
332 | * | ||
333 | * @return Number of loops | ||
334 | */ | ||
335 | 2372 | unsigned int Scope::getLoopNestingDepth() const { // NOLINT(misc-no-recursion) | |
336 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2372 times.
|
2372 | assert(parent != nullptr); |
337 |
2/2✓ Branch 0 taken 434 times.
✓ Branch 1 taken 1938 times.
|
2372 | if (parent->parent == nullptr) |
338 | 434 | return 0; | |
339 | 1938 | unsigned int loopCount = parent->getLoopNestingDepth(); | |
340 |
6/6✓ Branch 0 taken 1839 times.
✓ Branch 1 taken 99 times.
✓ Branch 2 taken 1189 times.
✓ Branch 3 taken 650 times.
✓ Branch 4 taken 24 times.
✓ Branch 5 taken 1165 times.
|
1938 | if (type == ScopeType::WHILE_BODY || type == ScopeType::FOR_BODY || type == ScopeType::FOREACH_BODY) |
341 | 773 | loopCount++; | |
342 | 1938 | return loopCount; | |
343 | } | ||
344 | |||
345 | /** | ||
346 | * Check if this scope is one of the child scopes of a switch statement | ||
347 | * | ||
348 | * @return Child scope of switch statement or not | ||
349 | */ | ||
350 | 7 | bool Scope::isInCaseBranch() const { // NOLINT(misc-no-recursion) | |
351 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | assert(parent != nullptr); |
352 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
|
7 | if (parent->parent == nullptr) |
353 | 2 | return false; | |
354 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | if (type == ScopeType::CASE_BODY) |
355 | 4 | return true; | |
356 | 1 | return parent->isInCaseBranch(); | |
357 | } | ||
358 | |||
359 | /** | ||
360 | * Check if this scope is within an async scope | ||
361 | * | ||
362 | * @return Within async scope or not | ||
363 | */ | ||
364 | 67 | bool Scope::isInAsyncScope() const { // NOLINT(misc-no-recursion) | |
365 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 59 times.
|
67 | if (isAsyncScope) |
366 | 8 | return true; | |
367 |
3/4✓ Branch 0 taken 37 times.
✓ Branch 1 taken 22 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 37 times.
|
59 | return parent != nullptr && parent->isInAsyncScope(); |
368 | } | ||
369 | |||
370 | /** | ||
371 | * Check if unsafe operations are allowed in this scope | ||
372 | * | ||
373 | * @return Allowed or not | ||
374 | */ | ||
375 | 3772 | bool Scope::doesAllowUnsafeOperations() const { // NOLINT(misc-no-recursion) | |
376 |
2/2✓ Branch 0 taken 2299 times.
✓ Branch 1 taken 1473 times.
|
3772 | if (type == ScopeType::UNSAFE_BODY) |
377 | 2299 | return true; | |
378 |
4/4✓ Branch 0 taken 1472 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 1471 times.
✓ Branch 4 taken 1 times.
|
1473 | return parent != nullptr && parent->doesAllowUnsafeOperations(); |
379 | } | ||
380 | |||
381 | /** | ||
382 | * Checks if this scope is imported | ||
383 | * | ||
384 | * @param askingScope Scope, which asks whether the current one is imported from its point of view or not | ||
385 | * | ||
386 | * @return Imported / not imported | ||
387 | */ | ||
388 | 73014 | bool Scope::isImportedBy(const Scope *askingScope) const { return askingScope->sourceFile->imports(sourceFile); } | |
389 | |||
390 | /** | ||
391 | * Get JSON representation of the symbol table | ||
392 | * | ||
393 | * @return Symbol table as JSON object | ||
394 | */ | ||
395 | 147115 | nlohmann::json Scope::getSymbolTableJSON() const { // NOLINT(misc-no-recursion) | |
396 |
1/2✓ Branch 1 taken 147115 times.
✗ Branch 2 not taken.
|
147115 | nlohmann::json result = symbolTable.toJSON(); |
397 | |||
398 | // Collect all children | ||
399 | 147115 | std::vector<nlohmann::json> jsonChildren; | |
400 |
1/2✓ Branch 2 taken 147115 times.
✗ Branch 3 not taken.
|
147115 | jsonChildren.reserve(children.size()); |
401 |
2/2✓ Branch 7 taken 143808 times.
✓ Branch 8 taken 147115 times.
|
290923 | for (const auto &[name, childScope] : children) { |
402 |
1/2✓ Branch 2 taken 143808 times.
✗ Branch 3 not taken.
|
143808 | nlohmann::json c = childScope->getSymbolTableJSON(); |
403 |
2/4✓ Branch 1 taken 143808 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 143808 times.
✗ Branch 5 not taken.
|
143808 | c["name"] = name; // Inject symbol table name into JSON object |
404 |
1/2✓ Branch 1 taken 143808 times.
✗ Branch 2 not taken.
|
143808 | jsonChildren.emplace_back(c); |
405 | 143808 | } | |
406 |
2/4✓ Branch 1 taken 147115 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 147115 times.
✗ Branch 5 not taken.
|
147115 | result["children"] = jsonChildren; |
407 | |||
408 | 294230 | return result; | |
409 | 147115 | } | |
410 | |||
411 | } // namespace spice::compiler | ||
412 |