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 |