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 | 18630 | Scope *Scope::createChildScope(const std::string &scopeName, ScopeType scopeType, const CodeLoc *declCodeLoc) { | |
21 |
3/6✓ Branch 0 (2→3) taken 18630 times.
✗ Branch 1 (2→14) not taken.
✓ Branch 2 (3→4) taken 18630 times.
✗ Branch 3 (3→12) not taken.
✓ Branch 4 (4→5) taken 18630 times.
✗ Branch 5 (4→10) not taken.
|
18630 | children.insert({scopeName, std::make_shared<Scope>(this, sourceFile, scopeType, declCodeLoc)}); |
22 | 18630 | 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 | 9791 | void Scope::renameChildScope(const std::string &oldName, const std::string &newName) { | |
33 |
4/8✓ Branch 0 (2→3) taken 9791 times.
✗ Branch 1 (2→19) not taken.
✓ Branch 2 (3→4) taken 9791 times.
✗ Branch 3 (3→7) not taken.
✓ Branch 4 (4→5) taken 9791 times.
✗ Branch 5 (4→19) not taken.
✓ Branch 6 (5→6) taken 9791 times.
✗ Branch 7 (5→7) not taken.
|
9791 | assert(children.contains(oldName) && !children.contains(newName)); |
34 |
1/2✓ Branch 0 (8→9) taken 9791 times.
✗ Branch 1 (8→19) not taken.
|
9791 | auto nodeHandler = children.extract(oldName); |
35 |
1/2✓ Branch 0 (10→11) taken 9791 times.
✗ Branch 1 (10→17) not taken.
|
9791 | nodeHandler.key() = newName; |
36 |
1/2✓ Branch 0 (12→13) taken 9791 times.
✗ Branch 1 (12→16) not taken.
|
9791 | children.insert(std::move(nodeHandler)); |
37 | 9791 | } | |
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 | 2897 | Scope *Scope::copyChildScope(const std::string &oldName, const std::string &newName) { | |
46 |
4/8✓ Branch 0 (2→3) taken 2897 times.
✗ Branch 1 (2→23) not taken.
✓ Branch 2 (3→4) taken 2897 times.
✗ Branch 3 (3→7) not taken.
✓ Branch 4 (4→5) taken 2897 times.
✗ Branch 5 (4→23) not taken.
✓ Branch 6 (5→6) taken 2897 times.
✗ Branch 7 (5→7) not taken.
|
2897 | assert(children.contains(oldName) && !children.contains(newName)); |
47 | // Create copy | ||
48 |
2/4✓ Branch 0 (8→9) taken 2897 times.
✗ Branch 1 (8→23) not taken.
✓ Branch 2 (10→11) taken 2897 times.
✗ Branch 3 (10→23) not taken.
|
2897 | const std::shared_ptr<Scope> newScope = children.at(oldName)->deepCopyScope(); |
49 | // Save copy under new name | ||
50 |
2/4✓ Branch 0 (11→12) taken 2897 times.
✗ Branch 1 (11→20) not taken.
✓ Branch 2 (12→13) taken 2897 times.
✗ Branch 3 (12→18) not taken.
|
2897 | children.insert({newName, newScope}); |
51 | 5794 | return newScope.get(); | |
52 | 2897 | } | |
53 | |||
54 | /** | ||
55 | * Deep copy the current scope and all its children | ||
56 | * | ||
57 | * @return Deep copy of the current scope | ||
58 | */ | ||
59 | 10303 | std::shared_ptr<Scope> Scope::deepCopyScope() { // NOLINT(misc-no-recursion) | |
60 | 10303 | const std::shared_ptr<Scope> newScope = std::make_shared<Scope>(*this); | |
61 |
2/2✓ Branch 0 (30→5) taken 7406 times.
✓ Branch 1 (30→31) taken 10303 times.
|
17709 | for (const auto &[childName, oldChild] : children) { |
62 |
2/4✓ Branch 0 (9→10) taken 7406 times.
✗ Branch 1 (9→37) not taken.
✓ Branch 2 (11→12) taken 7406 times.
✗ Branch 3 (11→35) not taken.
|
7406 | newScope->children[childName] = oldChild->deepCopyScope(); |
63 |
1/2✓ Branch 0 (16→17) taken 7406 times.
✗ Branch 1 (16→38) not taken.
|
7406 | newScope->children[childName]->parent = newScope.get(); |
64 |
2/4✓ Branch 0 (19→20) taken 7406 times.
✗ Branch 1 (19→38) not taken.
✓ Branch 2 (22→23) taken 7406 times.
✗ Branch 3 (22→38) not taken.
|
7406 | newScope->children[childName]->symbolTable.scope = newScope->children[childName].get(); |
65 |
1/2✓ Branch 0 (26→27) taken 7406 times.
✗ Branch 1 (26→38) not taken.
|
7406 | newScope->children[childName]->symbolTable.parent = &newScope->symbolTable; |
66 | } | ||
67 | 10303 | newScope->symbolTable.scope = newScope.get(); | |
68 | 10303 | 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 | 39211 | Scope *Scope::getChildScope(const std::string &scopeName) const { | |
78 |
5/6✓ Branch 0 (3→4) taken 39210 times.
✓ Branch 1 (3→7) taken 1 times.
✓ Branch 2 (5→6) taken 39210 times.
✗ Branch 3 (5→7) not taken.
✓ Branch 4 (8→9) taken 39210 times.
✓ Branch 5 (8→11) taken 1 times.
|
39211 | if (!children.empty() && children.contains(scopeName)) |
79 | 39210 | 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 | 16706 | std::vector<SymbolTableEntry *> Scope::getVarsGoingOutOfScope() { // NOLINT(misc-no-recursion) | |
89 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 16706 times.
|
16706 | assert(parent != nullptr); // Should not be called in root scope |
90 | 16706 | std::vector<SymbolTableEntry *> varsGoingOutOfScope; | |
91 | |||
92 | // Collect all variables in this scope | ||
93 |
2/2✓ Branch 0 (24→6) taken 29036 times.
✓ Branch 1 (24→25) taken 16706 times.
|
45742 | for (const auto &[name, entry] : symbolTable.symbols) { |
94 | // Skip 'this' and result variables | ||
95 |
8/10✓ Branch 0 (9→10) taken 29036 times.
✗ Branch 1 (9→48) not taken.
✓ Branch 2 (10→11) taken 23591 times.
✓ Branch 3 (10→13) taken 5445 times.
✓ Branch 4 (11→12) taken 23591 times.
✗ Branch 5 (11→48) not taken.
✓ Branch 6 (12→13) taken 6115 times.
✓ Branch 7 (12→14) taken 17476 times.
✓ Branch 8 (15→16) taken 11560 times.
✓ Branch 9 (15→17) taken 17476 times.
|
29036 | if (name == THIS_VARIABLE_NAME || name == RETURN_VARIABLE_NAME) |
96 | 11560 | continue; | |
97 | // Skip parameters (ToDo: Remove when copy constructors work for by-value argument passing) | ||
98 |
2/2✓ Branch 0 (17→18) taken 9968 times.
✓ Branch 1 (17→19) taken 7508 times.
|
17476 | if (entry.isParam) |
99 | 9968 | continue; | |
100 | // Found variable, that goes out of scope | ||
101 |
2/4✓ Branch 0 (19→20) taken 7508 times.
✗ Branch 1 (19→47) not taken.
✓ Branch 2 (20→21) taken 7508 times.
✗ Branch 3 (20→47) not taken.
|
7508 | 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 (25→26) taken 100 times.
✓ Branch 1 (25→45) taken 16606 times.
|
16706 | if (isDtorScope) { |
106 |
2/4✓ Branch 0 (26→27) taken 100 times.
✗ Branch 1 (26→29) not taken.
✓ Branch 2 (27→28) taken 100 times.
✗ Branch 3 (27→29) not taken.
|
100 | assert(parent != nullptr && parent->type == ScopeType::STRUCT); |
107 | // Get all fields of the struct | ||
108 |
2/2✓ Branch 0 (43→32) taken 3101 times.
✓ Branch 1 (43→44) taken 100 times.
|
3201 | for (const auto &[name, entry] : parent->symbolTable.symbols) |
109 |
4/6✓ Branch 0 (35→36) taken 3101 times.
✗ Branch 1 (35→51) not taken.
✓ Branch 2 (36→37) taken 3101 times.
✗ Branch 3 (36→49) not taken.
✓ Branch 4 (37→38) taken 271 times.
✓ Branch 5 (37→41) taken 2830 times.
|
3101 | if (!entry.getQualType().isOneOf({TY_FUNCTION, TY_PROCEDURE})) |
110 |
2/4✓ Branch 0 (38→39) taken 271 times.
✗ Branch 1 (38→50) not taken.
✓ Branch 2 (39→40) taken 271 times.
✗ Branch 3 (39→50) not taken.
|
271 | varsGoingOutOfScope.push_back(&parent->symbolTable.symbols.at(name)); |
111 | } | ||
112 | |||
113 | 16706 | 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 | 2765 | void Scope::insertGenericType(const std::string &typeName, const GenericType &genericType) { | |
123 |
1/2✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 2765 times.
|
2765 | assert(!genericTypes.contains(typeName)); |
124 |
2/4✓ Branch 0 (5→6) taken 2765 times.
✗ Branch 1 (5→11) not taken.
✓ Branch 2 (6→7) taken 2765 times.
✗ Branch 3 (6→9) not taken.
|
2765 | genericTypes.insert({typeName, genericType}); |
125 | 2765 | } | |
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 | 27837 | GenericType *Scope::lookupGenericType(const std::string &typeName) { // NOLINT(misc-no-recursion) | |
135 |
2/2✓ Branch 0 (3→4) taken 9184 times.
✓ Branch 1 (3→6) taken 18653 times.
|
27837 | if (genericTypes.contains(typeName)) |
136 | 9184 | return &genericTypes.at(typeName); | |
137 |
2/2✓ Branch 0 (6→7) taken 1906 times.
✓ Branch 1 (6→9) taken 16747 times.
|
18653 | 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 | 1382 | void Scope::collectWarnings(std::vector<CompilerWarning> &warnings) const { // NOLINT(misc-no-recursion) | |
147 | // Visit own symbols | ||
148 | CompilerWarningType warningType; | ||
149 | 1382 | std::string warningMessage; | |
150 |
5/8✓ Branch 0 (3→4) taken 1382 times.
✗ Branch 1 (3→211) not taken.
✓ Branch 2 (4→5) taken 1382 times.
✗ Branch 3 (4→211) not taken.
✓ Branch 4 (5→6) taken 1382 times.
✗ Branch 5 (5→211) not taken.
✓ Branch 6 (140→7) taken 2964 times.
✓ Branch 7 (140→141) taken 1382 times.
|
4346 | 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 | 2964 | const std::string &name = entry.name; | |
153 |
6/6✓ Branch 0 (8→9) taken 229 times.
✓ Branch 1 (8→11) taken 2735 times.
✓ Branch 2 (10→11) taken 60 times.
✓ Branch 3 (10→12) taken 169 times.
✓ Branch 4 (13→14) taken 2795 times.
✓ Branch 5 (13→15) taken 169 times.
|
2964 | if (entry.used || name.starts_with(UNUSED_VARIABLE_NAME)) |
154 | 2818 | continue; | |
155 | |||
156 |
1/2✓ Branch 0 (15→16) taken 169 times.
✗ Branch 1 (15→210) not taken.
|
169 | 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 (16→17) taken 28 times.
✓ Branch 1 (16→92) taken 51 times.
✓ Branch 2 (16→119) taken 37 times.
✗ Branch 3 (16→125) not taken.
✓ Branch 4 (16→128) taken 53 times.
|
169 | switch (type) { |
160 | 28 | case ScopeType::GLOBAL: { | |
161 | // Skip generic function/procedure/struct/interface entries | ||
162 |
6/10✓ Branch 0 (17→18) taken 28 times.
✗ Branch 1 (17→155) 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→155) 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()) |
163 | ✗ | continue; | |
164 | |||
165 |
3/4✓ Branch 0 (26→27) taken 28 times.
✗ Branch 1 (26→210) not taken.
✓ Branch 2 (27→28) taken 11 times.
✓ Branch 3 (27→38) taken 17 times.
|
28 | if (entryType.is(TY_FUNCTION)) { |
166 |
1/2✓ Branch 0 (28→29) taken 11 times.
✗ Branch 1 (28→210) not taken.
|
11 | const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name); |
167 | 11 | warningType = UNUSED_FUNCTION; | |
168 |
3/6✓ Branch 0 (30→31) taken 11 times.
✗ Branch 1 (30→160) not taken.
✓ Branch 2 (31→32) taken 11 times.
✗ Branch 3 (31→158) not taken.
✓ Branch 4 (32→33) taken 11 times.
✗ Branch 5 (32→156) not taken.
|
11 | warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused"; |
169 |
3/4✓ Branch 0 (38→39) taken 17 times.
✗ Branch 1 (38→210) not taken.
✓ Branch 2 (39→40) taken 2 times.
✓ Branch 3 (39→50) taken 15 times.
|
17 | } else if (entryType.is(TY_PROCEDURE)) { |
170 |
1/2✓ Branch 0 (40→41) taken 2 times.
✗ Branch 1 (40→210) not taken.
|
2 | const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name); |
171 | 2 | warningType = UNUSED_PROCEDURE; | |
172 |
3/6✓ Branch 0 (42→43) taken 2 times.
✗ Branch 1 (42→167) not taken.
✓ Branch 2 (43→44) taken 2 times.
✗ Branch 3 (43→165) not taken.
✓ Branch 4 (44→45) taken 2 times.
✗ Branch 5 (44→163) not taken.
|
2 | warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused"; |
173 |
3/4✓ Branch 0 (50→51) taken 15 times.
✗ Branch 1 (50→210) not taken.
✓ Branch 2 (51→52) taken 2 times.
✓ Branch 3 (51→58) taken 13 times.
|
15 | } else if (entryType.is(TY_STRUCT)) { |
174 | 2 | warningType = UNUSED_STRUCT; | |
175 |
2/4✓ Branch 0 (52→53) taken 2 times.
✗ Branch 1 (52→172) not taken.
✓ Branch 2 (53→54) taken 2 times.
✗ Branch 3 (53→170) not taken.
|
2 | warningMessage = "The struct '" + entry.name + "' is unused"; |
176 |
3/4✓ Branch 0 (58→59) taken 13 times.
✗ Branch 1 (58→210) not taken.
✓ Branch 2 (59→60) taken 1 times.
✓ Branch 3 (59→66) taken 12 times.
|
13 | } else if (entryType.is(TY_INTERFACE)) { |
177 | 1 | warningType = UNUSED_INTERFACE; | |
178 |
2/4✓ Branch 0 (60→61) taken 1 times.
✗ Branch 1 (60→176) not taken.
✓ Branch 2 (61→62) taken 1 times.
✗ Branch 3 (61→174) not taken.
|
1 | warningMessage = "The interface '" + entry.name + "' is unused"; |
179 |
3/4✓ Branch 0 (66→67) taken 12 times.
✗ Branch 1 (66→210) not taken.
✓ Branch 2 (67→68) taken 2 times.
✓ Branch 3 (67→69) taken 10 times.
|
12 | } else if (entryType.is(TY_ENUM)) { |
180 | 2 | continue; // Do not report unused enums. Only unused enum items are reported | |
181 |
3/4✓ Branch 0 (69→70) taken 10 times.
✗ Branch 1 (69→210) not taken.
✓ Branch 2 (70→71) taken 4 times.
✓ Branch 3 (70→77) taken 6 times.
|
10 | } else if (entryType.is(TY_IMPORT)) { |
182 | 4 | warningType = UNUSED_IMPORT; | |
183 |
2/4✓ Branch 0 (71→72) taken 4 times.
✗ Branch 1 (71→180) not taken.
✓ Branch 2 (72→73) taken 4 times.
✗ Branch 3 (72→178) not taken.
|
4 | warningMessage = "The import '" + entry.name + "' is unused"; |
184 |
3/4✓ Branch 0 (77→78) taken 6 times.
✗ Branch 1 (77→210) not taken.
✓ Branch 2 (78→79) taken 1 times.
✓ Branch 3 (78→85) taken 5 times.
|
6 | } else if (entryType.is(TY_ALIAS)) { |
185 | 1 | warningType = UNUSED_ALIAS; | |
186 |
2/4✓ Branch 0 (79→80) taken 1 times.
✗ Branch 1 (79→184) not taken.
✓ Branch 2 (80→81) taken 1 times.
✗ Branch 3 (80→182) not taken.
|
1 | warningMessage = "The type alias '" + entry.name + "' is unused"; |
187 | } else { | ||
188 | 5 | warningType = UNUSED_VARIABLE; | |
189 |
2/4✓ Branch 0 (85→86) taken 5 times.
✗ Branch 1 (85→188) not taken.
✓ Branch 2 (86→87) taken 5 times.
✗ Branch 3 (86→186) not taken.
|
5 | warningMessage = "The variable '" + entry.name + "' is unused"; |
190 | } | ||
191 | |||
192 | 26 | break; | |
193 | } | ||
194 | 51 | case ScopeType::STRUCT: // fall-through | |
195 | case ScopeType::INTERFACE: { | ||
196 |
3/4✓ Branch 0 (92→93) taken 51 times.
✗ Branch 1 (92→190) not taken.
✓ Branch 2 (93→94) taken 40 times.
✓ Branch 3 (93→112) taken 11 times.
|
51 | if (entryType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) { |
197 | // Skip implicit method entries and generic templates | ||
198 |
1/2✓ Branch 0 (94→95) taken 40 times.
✗ Branch 1 (94→210) not taken.
|
40 | const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name); |
199 |
5/6✓ Branch 0 (96→97) taken 19 times.
✓ Branch 1 (96→99) taken 21 times.
✗ Branch 2 (98→99) not taken.
✓ Branch 3 (98→100) taken 19 times.
✓ Branch 4 (101→102) taken 21 times.
✓ Branch 5 (101→103) taken 19 times.
|
40 | if (fctManifestations->empty() || fctManifestations->front()->implicitDefault) |
200 | 21 | continue; | |
201 | |||
202 | 19 | warningType = UNUSED_METHOD; | |
203 |
3/6✓ Branch 0 (104→105) taken 19 times.
✗ Branch 1 (104→195) not taken.
✓ Branch 2 (105→106) taken 19 times.
✗ Branch 3 (105→193) not taken.
✓ Branch 4 (106→107) taken 19 times.
✗ Branch 5 (106→191) not taken.
|
19 | warningMessage = "The method '" + fctManifestations->front()->getSignature() + "' is unused"; |
204 | } else { | ||
205 | 11 | warningType = UNUSED_FIELD; | |
206 |
2/4✓ Branch 0 (112→113) taken 11 times.
✗ Branch 1 (112→200) not taken.
✓ Branch 2 (113→114) taken 11 times.
✗ Branch 3 (113→198) not taken.
|
11 | warningMessage = "The field '" + entry.name + "' is unused"; |
207 | } | ||
208 | 30 | break; | |
209 | } | ||
210 | 37 | case ScopeType::ENUM: { | |
211 | 37 | warningType = UNUSED_ENUM_ITEM; | |
212 |
2/4✓ Branch 0 (119→120) taken 37 times.
✗ Branch 1 (119→204) not taken.
✓ Branch 2 (120→121) taken 37 times.
✗ Branch 3 (120→202) not taken.
|
37 | warningMessage = "The enum item '" + entry.name + "' is unused"; |
213 | 37 | 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 | 53 | warningType = UNUSED_VARIABLE; | |
223 |
2/4✓ Branch 0 (128→129) taken 53 times.
✗ Branch 1 (128→208) not taken.
✓ Branch 2 (129→130) taken 53 times.
✗ Branch 3 (129→206) not taken.
|
53 | warningMessage = "The variable '" + entry.name + "' is unused"; |
224 | 53 | break; | |
225 | } | ||
226 | } | ||
227 | |||
228 | // Add warning | ||
229 |
2/4✓ Branch 0 (134→135) taken 146 times.
✗ Branch 1 (134→210) not taken.
✓ Branch 2 (135→136) taken 146 times.
✗ Branch 3 (135→210) not taken.
|
146 | warnings.emplace_back(entry.getDeclCodeLoc(), warningType, warningMessage); |
230 | } | ||
231 | |||
232 | // Visit children | ||
233 |
5/8✓ Branch 0 (141→142) taken 1382 times.
✗ Branch 1 (141→212) not taken.
✓ Branch 2 (142→143) taken 1382 times.
✗ Branch 3 (142→212) not taken.
✓ Branch 4 (143→144) taken 1382 times.
✗ Branch 5 (143→212) not taken.
✓ Branch 6 (152→145) taken 1163 times.
✓ Branch 7 (152→153) taken 1382 times.
|
2545 | for (const auto &childScope : children | std::views::values) |
234 |
2/2✓ Branch 0 (147→148) taken 1128 times.
✓ Branch 1 (147→150) taken 35 times.
|
1163 | if (!childScope->isGenericScope) |
235 |
1/2✓ Branch 0 (149→150) taken 1128 times.
✗ Branch 1 (149→212) not taken.
|
1128 | childScope->collectWarnings(warnings); |
236 | 1382 | } | |
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 | 53880 | void Scope::ensureSuccessfulTypeInference() const { // NOLINT(misc-no-recursion) | |
243 | // Check symbols in this scope | ||
244 |
2/2✓ Branch 0 (19→4) taken 121811 times.
✓ Branch 1 (19→20) taken 53879 times.
|
175690 | for (auto &[name, entry] : symbolTable.symbols) |
245 |
4/6✓ Branch 0 (7→8) taken 121811 times.
✗ Branch 1 (7→40) not taken.
✓ Branch 2 (8→9) taken 121811 times.
✗ Branch 3 (8→40) not taken.
✓ Branch 4 (9→10) taken 1 times.
✓ Branch 5 (9→17) taken 121810 times.
|
121811 | if (entry.getQualType().is(TY_DYN)) |
246 |
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"); |
247 | |||
248 | // Check child scopes | ||
249 |
5/8✓ Branch 0 (20→21) taken 53879 times.
✗ Branch 1 (20→41) not taken.
✓ Branch 2 (21→22) taken 53879 times.
✗ Branch 3 (21→41) not taken.
✓ Branch 4 (22→23) taken 53879 times.
✗ Branch 5 (22→41) not taken.
✓ Branch 6 (29→24) taken 52232 times.
✓ Branch 7 (29→30) taken 53878 times.
|
106110 | for (const auto &scope : children | std::views::values) |
250 |
2/2✓ Branch 0 (26→27) taken 52231 times.
✓ Branch 1 (26→41) taken 1 times.
|
52232 | scope->ensureSuccessfulTypeInference(); |
251 | 53878 | } | |
252 | |||
253 | /** | ||
254 | * Get the number of fields if this is a struct scope | ||
255 | * | ||
256 | * @return Number of fields | ||
257 | */ | ||
258 | 40635 | size_t Scope::getFieldCount() const { | |
259 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 40635 times.
|
40635 | assert(type == ScopeType::STRUCT); |
260 | 40635 | size_t fieldCount = 0; | |
261 |
5/8✓ Branch 0 (4→5) taken 40635 times.
✗ Branch 1 (4→29) not taken.
✓ Branch 2 (5→6) taken 40635 times.
✗ Branch 3 (5→29) not taken.
✓ Branch 4 (6→7) taken 40635 times.
✗ Branch 5 (6→29) not taken.
✓ Branch 6 (26→8) taken 776641 times.
✓ Branch 7 (26→27) taken 40635 times.
|
817276 | for (const auto &symbol : symbolTable.symbols | std::views::values) { |
262 |
1/2✗ Branch 0 (9→10) not taken.
✓ Branch 1 (9→11) taken 776641 times.
|
776641 | if (symbol.anonymous) |
263 | ✗ | continue; | |
264 |
1/2✓ Branch 0 (11→12) taken 776641 times.
✗ Branch 1 (11→29) not taken.
|
776641 | const QualType &symbolType = symbol.getQualType(); |
265 |
2/4✓ Branch 0 (12→13) taken 776641 times.
✗ Branch 1 (12→29) not taken.
✗ Branch 2 (13→14) not taken.
✓ Branch 3 (13→15) taken 776641 times.
|
776641 | if (symbolType.is(TY_IMPORT)) |
266 | ✗ | continue; | |
267 | 776641 | const ASTNode *declNode = symbol.declNode; | |
268 |
8/10✓ Branch 0 (15→16) taken 776641 times.
✗ Branch 1 (15→29) not taken.
✓ Branch 2 (16→17) taken 165362 times.
✓ Branch 3 (16→19) taken 611279 times.
✓ Branch 4 (17→18) taken 165362 times.
✗ Branch 5 (17→29) not taken.
✓ Branch 6 (18→19) taken 29231 times.
✓ Branch 7 (18→20) taken 136131 times.
✓ Branch 8 (21→22) taken 640510 times.
✓ Branch 9 (21→23) taken 136131 times.
|
776641 | if (declNode->isFctOrProcDef() || declNode->isStructDef()) |
269 | 640510 | continue; | |
270 | 136131 | fieldCount++; | |
271 | } | ||
272 | 40635 | 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 | 550 | std::vector<Function *> Scope::getVirtualMethods() { | |
281 |
3/4✓ Branch 0 (2→3) taken 240 times.
✓ Branch 1 (2→5) taken 310 times.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 240 times.
|
550 | assert(type == ScopeType::STRUCT || type == ScopeType::INTERFACE); |
282 | |||
283 | // Collect all virtual methods | ||
284 | 550 | std::vector<Function *> methods; | |
285 |
2/2✓ Branch 0 (37→7) taken 3630 times.
✓ Branch 1 (37→38) taken 550 times.
|
4180 | for (auto &[fctId, manifestationList] : functions) { |
286 |
1/2✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→13) taken 3630 times.
|
3630 | assert(!manifestationList.empty()); |
287 |
2/2✓ Branch 0 (34→15) taken 5286 times.
✓ Branch 1 (34→35) taken 3630 times.
|
8916 | for (auto &[mangledName, function] : manifestationList) |
288 |
2/2✓ Branch 0 (27→28) taken 1220 times.
✓ Branch 1 (27→32) taken 4066 times.
|
5286 | if (function.isVirtualMethod()) |
289 |
3/6✓ Branch 0 (28→29) taken 1220 times.
✗ Branch 1 (28→41) not taken.
✓ Branch 2 (29→30) taken 1220 times.
✗ Branch 3 (29→41) not taken.
✓ Branch 4 (30→31) taken 1220 times.
✗ Branch 5 (30→41) not taken.
|
1220 | methods.push_back(&functions.at(fctId).at(mangledName)); |
290 | } | ||
291 | |||
292 | // Sort the list | ||
293 |
2/4✓ Branch 0 (38→39) taken 550 times.
✗ Branch 1 (38→44) not taken.
✗ Branch 2 (4→5) not taken.
✓ Branch 3 (4→6) taken 1492 times.
|
3534 | std::ranges::sort(methods, [](const Function *a, const Function *b) { return a->getDeclCodeLoc() < b->getDeclCodeLoc(); }); |
294 | |||
295 | 550 | 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 | 974 | std::vector<const Struct *> Scope::getAllStructManifestationsInDeclarationOrder() const { | |
304 | // Retrieve all struct manifestations in this scope | ||
305 | 974 | std::vector<const Struct *> manifestations; | |
306 |
1/2✓ Branch 0 (3→4) taken 974 times.
✗ Branch 1 (3→27) not taken.
|
974 | manifestations.reserve(structs.size()); // Reserve at least the size of individual generic structs |
307 |
5/8✓ Branch 0 (4→5) taken 974 times.
✗ Branch 1 (4→26) not taken.
✓ Branch 2 (5→6) taken 974 times.
✗ Branch 3 (5→26) not taken.
✓ Branch 4 (6→7) taken 974 times.
✗ Branch 5 (6→26) not taken.
✓ Branch 6 (20→8) taken 579 times.
✓ Branch 7 (20→21) taken 974 times.
|
1553 | for (const auto &structManifestations : structs | std::views::values) |
308 |
5/8✓ Branch 0 (9→10) taken 579 times.
✗ Branch 1 (9→25) not taken.
✓ Branch 2 (10→11) taken 579 times.
✗ Branch 3 (10→25) not taken.
✓ Branch 4 (11→12) taken 579 times.
✗ Branch 5 (11→25) not taken.
✓ Branch 6 (17→13) taken 580 times.
✓ Branch 7 (17→18) taken 579 times.
|
1159 | for (const auto &manifestation : structManifestations | std::views::values) |
309 |
1/2✓ Branch 0 (14→15) taken 580 times.
✗ Branch 1 (14→24) not taken.
|
580 | manifestations.push_back(&manifestation); |
310 | |||
311 | // Sort manifestations by declaration code location | ||
312 |
2/2✓ Branch 0 (4→5) taken 2 times.
✓ Branch 1 (4→6) taken 283 times.
|
570 | auto sortLambda = [](const Struct *lhs, const Struct *rhs) { return lhs->getDeclCodeLoc() < rhs->getDeclCodeLoc(); }; |
313 |
1/2✓ Branch 0 (21→22) taken 974 times.
✗ Branch 1 (21→27) not taken.
|
974 | std::ranges::sort(manifestations, sortLambda); |
314 | 974 | return manifestations; | |
315 | ✗ | } | |
316 | |||
317 | /** | ||
318 | * Check if this struct has any reference fields | ||
319 | * | ||
320 | * @return Has reference fields or not | ||
321 | */ | ||
322 | 298 | bool Scope::hasRefFields() { | |
323 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 298 times.
|
298 | assert(type == ScopeType::STRUCT); |
324 |
2/2✓ Branch 0 (16→5) taken 788 times.
✓ Branch 1 (16→17) taken 291 times.
|
1079 | for (size_t i = 0; i < getFieldCount(); i++) |
325 |
3/4✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 788 times.
✓ Branch 2 (12→13) taken 7 times.
✓ Branch 3 (12→14) taken 781 times.
|
1576 | if (lookupField(i)->getQualType().isRef()) |
326 | 7 | return true; | |
327 | 291 | return false; | |
328 | } | ||
329 | |||
330 | /** | ||
331 | * Get the current number of nested loops | ||
332 | * | ||
333 | * @return Number of loops | ||
334 | */ | ||
335 | 2484 | unsigned int Scope::getLoopNestingDepth() const { // NOLINT(misc-no-recursion) | |
336 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 2484 times.
|
2484 | assert(parent != nullptr); |
337 |
2/2✓ Branch 0 (4→5) taken 454 times.
✓ Branch 1 (4→6) taken 2030 times.
|
2484 | if (parent->parent == nullptr) |
338 | 454 | return 0; | |
339 | 2030 | unsigned int loopCount = parent->getLoopNestingDepth(); | |
340 |
6/6✓ Branch 0 (7→8) taken 1927 times.
✓ Branch 1 (7→10) taken 103 times.
✓ Branch 2 (8→9) taken 1245 times.
✓ Branch 3 (8→10) taken 682 times.
✓ Branch 4 (9→10) taken 24 times.
✓ Branch 5 (9→11) taken 1221 times.
|
2030 | if (type == ScopeType::WHILE_BODY || type == ScopeType::FOR_BODY || type == ScopeType::FOREACH_BODY) |
341 | 809 | loopCount++; | |
342 | 2030 | 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 (2→3) not taken.
✓ Branch 1 (2→4) taken 7 times.
|
7 | assert(parent != nullptr); |
352 |
2/2✓ Branch 0 (4→5) taken 2 times.
✓ Branch 1 (4→6) taken 5 times.
|
7 | if (parent->parent == nullptr) |
353 | 2 | return false; | |
354 |
2/2✓ Branch 0 (6→7) taken 4 times.
✓ Branch 1 (6→8) 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 | 75 | bool Scope::isInAsyncScope() const { // NOLINT(misc-no-recursion) | |
365 |
2/2✓ Branch 0 (2→3) taken 8 times.
✓ Branch 1 (2→4) taken 67 times.
|
75 | if (isAsyncScope) |
366 | 8 | return true; | |
367 |
3/4✓ Branch 0 (4→5) taken 43 times.
✓ Branch 1 (4→8) taken 24 times.
✗ Branch 2 (6→7) not taken.
✓ Branch 3 (6→8) taken 43 times.
|
67 | 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 | 4593 | bool Scope::doesAllowUnsafeOperations() const { // NOLINT(misc-no-recursion) | |
376 |
2/2✓ Branch 0 (2→3) taken 2882 times.
✓ Branch 1 (2→4) taken 1711 times.
|
4593 | if (type == ScopeType::UNSAFE_BODY) |
377 | 2882 | return true; | |
378 |
4/4✓ Branch 0 (4→5) taken 1710 times.
✓ Branch 1 (4→8) taken 1 times.
✓ Branch 2 (6→7) taken 1709 times.
✓ Branch 3 (6→8) taken 1 times.
|
1711 | 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 | 78889 | 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 | 53944 | nlohmann::json Scope::getSymbolTableJSON() const { // NOLINT(misc-no-recursion) | |
396 |
1/2✓ Branch 0 (2→3) taken 53944 times.
✗ Branch 1 (2→42) not taken.
|
53944 | nlohmann::json result = symbolTable.toJSON(); |
397 | |||
398 | // Collect all children | ||
399 | 53944 | std::vector<nlohmann::json> jsonChildren; | |
400 |
1/2✓ Branch 0 (4→5) taken 53944 times.
✗ Branch 1 (4→38) not taken.
|
53944 | jsonChildren.reserve(children.size()); |
401 |
2/2✓ Branch 0 (20→7) taken 52288 times.
✓ Branch 1 (20→21) taken 53944 times.
|
106232 | for (const auto &[name, childScope] : children) { |
402 |
1/2✓ Branch 0 (11→12) taken 52288 times.
✗ Branch 1 (11→33) not taken.
|
52288 | nlohmann::json c = childScope->getSymbolTableJSON(); |
403 |
2/4✓ Branch 0 (12→13) taken 52288 times.
✗ Branch 1 (12→30) not taken.
✓ Branch 2 (13→14) taken 52288 times.
✗ Branch 3 (13→28) not taken.
|
52288 | c["name"] = name; // Inject symbol table name into JSON object |
404 |
1/2✓ Branch 0 (16→17) taken 52288 times.
✗ Branch 1 (16→31) not taken.
|
52288 | jsonChildren.emplace_back(c); |
405 | 52288 | } | |
406 |
2/4✓ Branch 0 (21→22) taken 53944 times.
✗ Branch 1 (21→37) not taken.
✓ Branch 2 (22→23) taken 53944 times.
✗ Branch 3 (22→35) not taken.
|
53944 | result["children"] = jsonChildren; |
407 | |||
408 | 53944 | return result; | |
409 | 53944 | } | |
410 | |||
411 | } // namespace spice::compiler | ||
412 |