| 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 | 24542 | Scope *Scope::createChildScope(const std::string &scopeName, ScopeType scopeType, const CodeLoc *declCodeLoc) { | |
| 21 |
3/6✓ Branch 2 → 3 taken 24542 times.
✗ Branch 2 → 14 not taken.
✓ Branch 3 → 4 taken 24542 times.
✗ Branch 3 → 12 not taken.
✓ Branch 4 → 5 taken 24542 times.
✗ Branch 4 → 10 not taken.
|
24542 | children.insert({scopeName, std::make_shared<Scope>(this, sourceFile, scopeType, declCodeLoc)}); |
| 22 | 24542 | 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 | 13240 | void Scope::renameChildScope(const std::string &oldName, const std::string &newName) { | |
| 33 |
4/8✓ Branch 2 → 3 taken 13240 times.
✗ Branch 2 → 19 not taken.
✓ Branch 3 → 4 taken 13240 times.
✗ Branch 3 → 7 not taken.
✓ Branch 4 → 5 taken 13240 times.
✗ Branch 4 → 19 not taken.
✓ Branch 5 → 6 taken 13240 times.
✗ Branch 5 → 7 not taken.
|
13240 | assert(children.contains(oldName) && !children.contains(newName)); |
| 34 |
1/2✓ Branch 8 → 9 taken 13240 times.
✗ Branch 8 → 19 not taken.
|
13240 | auto nodeHandler = children.extract(oldName); |
| 35 |
1/2✓ Branch 10 → 11 taken 13240 times.
✗ Branch 10 → 17 not taken.
|
13240 | nodeHandler.key() = newName; |
| 36 |
1/2✓ Branch 12 → 13 taken 13240 times.
✗ Branch 12 → 16 not taken.
|
13240 | children.insert(std::move(nodeHandler)); |
| 37 | 13240 | } | |
| 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 | 4541 | Scope *Scope::copyChildScope(const std::string &oldName, const std::string &newName) { | |
| 46 |
4/8✓ Branch 2 → 3 taken 4541 times.
✗ Branch 2 → 23 not taken.
✓ Branch 3 → 4 taken 4541 times.
✗ Branch 3 → 7 not taken.
✓ Branch 4 → 5 taken 4541 times.
✗ Branch 4 → 23 not taken.
✓ Branch 5 → 6 taken 4541 times.
✗ Branch 5 → 7 not taken.
|
4541 | assert(children.contains(oldName) && !children.contains(newName)); |
| 47 | // Create copy | ||
| 48 |
2/4✓ Branch 8 → 9 taken 4541 times.
✗ Branch 8 → 23 not taken.
✓ Branch 10 → 11 taken 4541 times.
✗ Branch 10 → 23 not taken.
|
4541 | const std::shared_ptr<Scope> newScope = children.at(oldName)->deepCopyScope(); |
| 49 | // Save copy under new name | ||
| 50 |
2/4✓ Branch 11 → 12 taken 4541 times.
✗ Branch 11 → 20 not taken.
✓ Branch 12 → 13 taken 4541 times.
✗ Branch 12 → 18 not taken.
|
4541 | children.insert({newName, newScope}); |
| 51 | 9082 | return newScope.get(); | |
| 52 | 4541 | } | |
| 53 | |||
| 54 | /** | ||
| 55 | * Deep copy the current scope and all its children | ||
| 56 | * | ||
| 57 | * @return Deep copy of the current scope | ||
| 58 | */ | ||
| 59 | 14463 | std::shared_ptr<Scope> Scope::deepCopyScope() { // NOLINT(misc-no-recursion) | |
| 60 | 14463 | const std::shared_ptr<Scope> newScope = std::make_shared<Scope>(*this); | |
| 61 |
2/2✓ Branch 30 → 5 taken 9922 times.
✓ Branch 30 → 31 taken 14463 times.
|
24385 | for (const auto &[childName, oldChild] : children) { |
| 62 |
2/4✓ Branch 9 → 10 taken 9922 times.
✗ Branch 9 → 37 not taken.
✓ Branch 11 → 12 taken 9922 times.
✗ Branch 11 → 35 not taken.
|
9922 | newScope->children[childName] = oldChild->deepCopyScope(); |
| 63 |
1/2✓ Branch 16 → 17 taken 9922 times.
✗ Branch 16 → 38 not taken.
|
9922 | newScope->children[childName]->parent = newScope.get(); |
| 64 |
2/4✓ Branch 19 → 20 taken 9922 times.
✗ Branch 19 → 38 not taken.
✓ Branch 22 → 23 taken 9922 times.
✗ Branch 22 → 38 not taken.
|
9922 | newScope->children[childName]->symbolTable.scope = newScope->children[childName].get(); |
| 65 |
1/2✓ Branch 26 → 27 taken 9922 times.
✗ Branch 26 → 38 not taken.
|
9922 | newScope->children[childName]->symbolTable.parent = &newScope->symbolTable; |
| 66 | } | ||
| 67 | 14463 | newScope->symbolTable.scope = newScope.get(); | |
| 68 | 14463 | 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 | 48706 | Scope *Scope::getChildScope(const std::string &scopeName) const { | |
| 78 |
5/6✓ Branch 3 → 4 taken 48705 times.
✓ Branch 3 → 7 taken 1 time.
✓ Branch 5 → 6 taken 48705 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 48705 times.
✓ Branch 8 → 11 taken 1 time.
|
48706 | if (!children.empty() && children.contains(scopeName)) |
| 79 | 48705 | 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 | 21294 | std::vector<SymbolTableEntry *> Scope::getVarsGoingOutOfScope() { // NOLINT(misc-no-recursion) | |
| 89 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 21294 times.
|
21294 | assert(!isRootScope()); // Should not be called in root scope |
| 90 | 21294 | std::vector<SymbolTableEntry *> varsGoingOutOfScope; | |
| 91 | |||
| 92 | // Collect all variables in this scope | ||
| 93 |
2/2✓ Branch 26 → 8 taken 38031 times.
✓ Branch 26 → 27 taken 21294 times.
|
59325 | for (const auto &[name, entry] : symbolTable.symbols) { |
| 94 | // Skip 'this' and result variables | ||
| 95 |
8/10✓ Branch 11 → 12 taken 38031 times.
✗ Branch 11 → 52 not taken.
✓ Branch 12 → 13 taken 31615 times.
✓ Branch 12 → 15 taken 6416 times.
✓ Branch 13 → 14 taken 31615 times.
✗ Branch 13 → 52 not taken.
✓ Branch 14 → 15 taken 7978 times.
✓ Branch 14 → 16 taken 23637 times.
✓ Branch 17 → 18 taken 14394 times.
✓ Branch 17 → 19 taken 23637 times.
|
38031 | if (name == THIS_VARIABLE_NAME || name == RETURN_VARIABLE_NAME) |
| 96 | 14394 | continue; | |
| 97 | // Skip parameters (ToDo: Remove when copy constructors work for by-value argument passing) | ||
| 98 |
2/2✓ Branch 19 → 20 taken 15218 times.
✓ Branch 19 → 21 taken 8419 times.
|
23637 | if (entry.isParam) |
| 99 | 15218 | continue; | |
| 100 | // Found variable, that goes out of scope | ||
| 101 |
2/4✓ Branch 21 → 22 taken 8419 times.
✗ Branch 21 → 51 not taken.
✓ Branch 22 → 23 taken 8419 times.
✗ Branch 22 → 51 not taken.
|
8419 | 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 27 → 28 taken 115 times.
✓ Branch 27 → 49 taken 21179 times.
|
21294 | if (isDtorScope) { |
| 106 |
2/4✓ Branch 30 → 31 taken 115 times.
✗ Branch 30 → 33 not taken.
✓ Branch 31 → 32 taken 115 times.
✗ Branch 31 → 33 not taken.
|
115 | assert(!isRootScope() && parent->type == ScopeType::STRUCT); |
| 107 | // Get all fields of the struct | ||
| 108 |
2/2✓ Branch 47 → 36 taken 3935 times.
✓ Branch 47 → 48 taken 115 times.
|
4050 | for (const auto &[name, entry] : parent->symbolTable.symbols) |
| 109 |
4/6✓ Branch 39 → 40 taken 3935 times.
✗ Branch 39 → 55 not taken.
✓ Branch 40 → 41 taken 3935 times.
✗ Branch 40 → 53 not taken.
✓ Branch 41 → 42 taken 311 times.
✓ Branch 41 → 45 taken 3624 times.
|
3935 | if (!entry.getQualType().isOneOf({TY_FUNCTION, TY_PROCEDURE})) |
| 110 |
2/4✓ Branch 42 → 43 taken 311 times.
✗ Branch 42 → 54 not taken.
✓ Branch 43 → 44 taken 311 times.
✗ Branch 43 → 54 not taken.
|
311 | varsGoingOutOfScope.push_back(&parent->symbolTable.symbols.at(name)); |
| 111 | } | ||
| 112 | |||
| 113 | 21294 | 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 | 5148 | void Scope::insertGenericType(const std::string &typeName, const GenericType &genericType) { | |
| 123 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 5148 times.
|
5148 | assert(!genericTypes.contains(typeName)); |
| 124 |
2/4✓ Branch 5 → 6 taken 5148 times.
✗ Branch 5 → 11 not taken.
✓ Branch 6 → 7 taken 5148 times.
✗ Branch 6 → 9 not taken.
|
5148 | genericTypes.insert({typeName, genericType}); |
| 125 | 5148 | } | |
| 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 | 38514 | GenericType *Scope::lookupGenericTypeStrict(const std::string &typeName) { | |
| 135 |
2/2✓ Branch 3 → 4 taken 16670 times.
✓ Branch 3 → 6 taken 21844 times.
|
38514 | 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 | 2221 | void Scope::collectWarnings(std::vector<CompilerWarning> &warnings) const { // NOLINT(misc-no-recursion) | |
| 145 | // Visit own symbols | ||
| 146 |
5/8✓ Branch 2 → 3 taken 2221 times.
✗ Branch 2 → 215 not taken.
✓ Branch 3 → 4 taken 2221 times.
✗ Branch 3 → 215 not taken.
✓ Branch 4 → 5 taken 2221 times.
✗ Branch 4 → 215 not taken.
✓ Branch 143 → 6 taken 6912 times.
✓ Branch 143 → 144 taken 2221 times.
|
9133 | for (const SymbolTableEntry &entry : symbolTable.symbols | std::views::values) { |
| 147 | // Do not produce a warning if the symbol is used or has a special name | ||
| 148 | 6912 | const std::string &name = entry.name; | |
| 149 |
6/6✓ Branch 7 → 8 taken 240 times.
✓ Branch 7 → 10 taken 6672 times.
✓ Branch 9 → 10 taken 99 times.
✓ Branch 9 → 11 taken 141 times.
✓ Branch 12 → 13 taken 6771 times.
✓ Branch 12 → 14 taken 141 times.
|
6912 | if (entry.used || name.starts_with(UNUSED_VARIABLE_NAME)) |
| 150 | 6795 | continue; | |
| 151 | |||
| 152 |
1/2✓ Branch 14 → 15 taken 141 times.
✗ Branch 14 → 214 not taken.
|
141 | const QualType entryType = entry.getQualType(); |
| 153 | |||
| 154 | // Determine warning type and message by the scope type and the symbol type | ||
| 155 | 141 | CompilerWarningType warningType = UNUSED_VARIABLE; | |
| 156 | 141 | std::string warningMessage; | |
| 157 |
4/4✓ Branch 16 → 17 taken 24 times.
✓ Branch 16 → 92 taken 45 times.
✓ Branch 16 → 121 taken 27 times.
✓ Branch 16 → 127 taken 45 times.
|
141 | switch (type) { |
| 158 | 24 | case ScopeType::GLOBAL: { | |
| 159 | // Skip generic function/procedure/struct/interface entries | ||
| 160 |
6/10✓ Branch 17 → 18 taken 24 times.
✗ Branch 17 → 157 not taken.
✓ Branch 18 → 19 taken 14 times.
✓ Branch 18 → 23 taken 10 times.
✓ Branch 19 → 20 taken 14 times.
✗ Branch 19 → 157 not taken.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 14 times.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 24 times.
|
24 | if (entryType.isOneOf({TY_FUNCTION, TY_PROCEDURE, TY_STRUCT, TY_INTERFACE}) && !entryType.getTemplateTypes().empty()) |
| 161 | ✗ | continue; | |
| 162 | |||
| 163 |
3/4✓ Branch 26 → 27 taken 24 times.
✗ Branch 26 → 212 not taken.
✓ Branch 27 → 28 taken 11 times.
✓ Branch 27 → 38 taken 13 times.
|
24 | if (entryType.is(TY_FUNCTION)) { |
| 164 |
1/2✓ Branch 28 → 29 taken 11 times.
✗ Branch 28 → 212 not taken.
|
11 | const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name); |
| 165 | 11 | warningType = UNUSED_FUNCTION; | |
| 166 |
3/6✓ Branch 30 → 31 taken 11 times.
✗ Branch 30 → 162 not taken.
✓ Branch 31 → 32 taken 11 times.
✗ Branch 31 → 160 not taken.
✓ Branch 32 → 33 taken 11 times.
✗ Branch 32 → 158 not taken.
|
11 | warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused"; |
| 167 |
3/4✓ Branch 38 → 39 taken 13 times.
✗ Branch 38 → 212 not taken.
✓ Branch 39 → 40 taken 1 time.
✓ Branch 39 → 50 taken 12 times.
|
13 | } else if (entryType.is(TY_PROCEDURE)) { |
| 168 |
1/2✓ Branch 40 → 41 taken 1 time.
✗ Branch 40 → 212 not taken.
|
1 | const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name); |
| 169 | 1 | warningType = UNUSED_PROCEDURE; | |
| 170 |
3/6✓ Branch 42 → 43 taken 1 time.
✗ Branch 42 → 169 not taken.
✓ Branch 43 → 44 taken 1 time.
✗ Branch 43 → 167 not taken.
✓ Branch 44 → 45 taken 1 time.
✗ Branch 44 → 165 not taken.
|
1 | warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused"; |
| 171 |
3/4✓ Branch 50 → 51 taken 12 times.
✗ Branch 50 → 212 not taken.
✓ Branch 51 → 52 taken 1 time.
✓ Branch 51 → 58 taken 11 times.
|
12 | } else if (entryType.is(TY_STRUCT)) { |
| 172 | 1 | warningType = UNUSED_STRUCT; | |
| 173 |
2/4✓ Branch 52 → 53 taken 1 time.
✗ Branch 52 → 174 not taken.
✓ Branch 53 → 54 taken 1 time.
✗ Branch 53 → 172 not taken.
|
1 | warningMessage = "The struct '" + entry.name + "' is unused"; |
| 174 |
3/4✓ Branch 58 → 59 taken 11 times.
✗ Branch 58 → 212 not taken.
✓ Branch 59 → 60 taken 1 time.
✓ Branch 59 → 66 taken 10 times.
|
11 | } else if (entryType.is(TY_INTERFACE)) { |
| 175 | 1 | warningType = UNUSED_INTERFACE; | |
| 176 |
2/4✓ Branch 60 → 61 taken 1 time.
✗ Branch 60 → 178 not taken.
✓ Branch 61 → 62 taken 1 time.
✗ Branch 61 → 176 not taken.
|
1 | warningMessage = "The interface '" + entry.name + "' is unused"; |
| 177 |
3/4✓ Branch 66 → 67 taken 10 times.
✗ Branch 66 → 212 not taken.
✓ Branch 67 → 68 taken 2 times.
✓ Branch 67 → 69 taken 8 times.
|
10 | } else if (entryType.is(TY_ENUM)) { |
| 178 | 2 | continue; // Do not report unused enums. Only unused enum items are reported | |
| 179 |
3/4✓ Branch 69 → 70 taken 8 times.
✗ Branch 69 → 212 not taken.
✓ Branch 70 → 71 taken 2 times.
✓ Branch 70 → 77 taken 6 times.
|
8 | } else if (entryType.is(TY_IMPORT)) { |
| 180 | 2 | warningType = UNUSED_IMPORT; | |
| 181 |
2/4✓ Branch 71 → 72 taken 2 times.
✗ Branch 71 → 182 not taken.
✓ Branch 72 → 73 taken 2 times.
✗ Branch 72 → 180 not taken.
|
2 | warningMessage = "The import '" + entry.name + "' is unused"; |
| 182 |
3/4✓ Branch 77 → 78 taken 6 times.
✗ Branch 77 → 212 not taken.
✓ Branch 78 → 79 taken 1 time.
✓ Branch 78 → 85 taken 5 times.
|
6 | } else if (entryType.is(TY_ALIAS)) { |
| 183 | 1 | warningType = UNUSED_ALIAS; | |
| 184 |
2/4✓ Branch 79 → 80 taken 1 time.
✗ Branch 79 → 186 not taken.
✓ Branch 80 → 81 taken 1 time.
✗ Branch 80 → 184 not taken.
|
1 | warningMessage = "The type alias '" + entry.name + "' is unused"; |
| 185 | } else { | ||
| 186 | 5 | warningType = UNUSED_VARIABLE; | |
| 187 |
2/4✓ Branch 85 → 86 taken 5 times.
✗ Branch 85 → 190 not taken.
✓ Branch 86 → 87 taken 5 times.
✗ Branch 86 → 188 not taken.
|
5 | warningMessage = "The variable '" + entry.name + "' is unused"; |
| 188 | } | ||
| 189 | |||
| 190 | 22 | break; | |
| 191 | } | ||
| 192 | 45 | case ScopeType::STRUCT: // fall-through | |
| 193 | case ScopeType::INTERFACE: { | ||
| 194 |
3/4✓ Branch 92 → 93 taken 45 times.
✗ Branch 92 → 212 not taken.
✓ Branch 93 → 94 taken 4 times.
✓ Branch 93 → 100 taken 41 times.
|
45 | if (entry.isField()) { |
| 195 | 4 | warningType = UNUSED_FIELD; | |
| 196 |
2/4✓ Branch 94 → 95 taken 4 times.
✗ Branch 94 → 194 not taken.
✓ Branch 95 → 96 taken 4 times.
✗ Branch 95 → 192 not taken.
|
4 | warningMessage = "The field '" + entry.name + "' is unused"; |
| 197 |
2/4✓ Branch 100 → 101 taken 41 times.
✗ Branch 100 → 196 not taken.
✓ Branch 101 → 102 taken 41 times.
✗ Branch 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 102 → 103 taken 41 times.
✗ Branch 102 → 212 not taken.
|
41 | const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name); |
| 200 |
5/6✓ Branch 104 → 105 taken 19 times.
✓ Branch 104 → 107 taken 22 times.
✗ Branch 106 → 107 not taken.
✓ Branch 106 → 108 taken 19 times.
✓ Branch 109 → 110 taken 22 times.
✓ Branch 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 112 → 113 taken 19 times.
✗ Branch 112 → 201 not taken.
✓ Branch 113 → 114 taken 19 times.
✗ Branch 113 → 199 not taken.
✓ Branch 114 → 115 taken 19 times.
✗ Branch 114 → 197 not taken.
|
19 | warningMessage = "The method '" + fctManifestations->front()->getSignature() + "' is unused"; |
| 205 | } | ||
| 206 | 23 | break; | |
| 207 | } | ||
| 208 | 27 | case ScopeType::ENUM: { | |
| 209 | 27 | warningType = UNUSED_ENUM_ITEM; | |
| 210 |
2/4✓ Branch 121 → 122 taken 27 times.
✗ Branch 121 → 206 not taken.
✓ Branch 122 → 123 taken 27 times.
✗ Branch 122 → 204 not taken.
|
27 | warningMessage = "The enum item '" + entry.name + "' is unused"; |
| 211 | 27 | break; | |
| 212 | } | ||
| 213 | 45 | default: { | |
| 214 | 45 | warningType = UNUSED_VARIABLE; | |
| 215 |
2/4✓ Branch 127 → 128 taken 45 times.
✗ Branch 127 → 210 not taken.
✓ Branch 128 → 129 taken 45 times.
✗ Branch 128 → 208 not taken.
|
45 | warningMessage = "The variable '" + entry.name + "' is unused"; |
| 216 | 45 | break; | |
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | // Add warning | ||
| 221 |
2/4✓ Branch 133 → 134 taken 117 times.
✗ Branch 133 → 212 not taken.
✓ Branch 134 → 135 taken 117 times.
✗ Branch 134 → 212 not taken.
|
117 | warnings.emplace_back(entry.getDeclCodeLoc(), warningType, warningMessage); |
| 222 |
2/2✓ Branch 137 → 138 taken 117 times.
✓ Branch 137 → 140 taken 24 times.
|
141 | } |
| 223 | |||
| 224 | // Visit children | ||
| 225 |
5/8✓ Branch 144 → 145 taken 2221 times.
✗ Branch 144 → 216 not taken.
✓ Branch 145 → 146 taken 2221 times.
✗ Branch 145 → 216 not taken.
✓ Branch 146 → 147 taken 2221 times.
✗ Branch 146 → 216 not taken.
✓ Branch 155 → 148 taken 2032 times.
✓ Branch 155 → 156 taken 2221 times.
|
4253 | for (const auto &childScope : children | std::views::values) |
| 226 |
2/2✓ Branch 150 → 151 taken 1934 times.
✓ Branch 150 → 153 taken 98 times.
|
2032 | if (!childScope->isGenericScope) |
| 227 |
1/2✓ Branch 152 → 153 taken 1934 times.
✗ Branch 152 → 216 not taken.
|
1934 | childScope->collectWarnings(warnings); |
| 228 | 2221 | } | |
| 229 | |||
| 230 | /** | ||
| 231 | * Checks if all variables of this and all child scopes are of an explicit type. | ||
| 232 | * This is executed after type inference to check that all variables could be inferred correctly. | ||
| 233 | */ | ||
| 234 | 79824 | void Scope::ensureSuccessfulTypeInference() const { // NOLINT(misc-no-recursion) | |
| 235 | // Check symbols in this scope | ||
| 236 |
2/2✓ Branch 19 → 4 taken 179789 times.
✓ Branch 19 → 20 taken 79823 times.
|
259612 | for (auto &[name, entry] : symbolTable.symbols) |
| 237 |
4/6✓ Branch 7 → 8 taken 179789 times.
✗ Branch 7 → 40 not taken.
✓ Branch 8 → 9 taken 179789 times.
✗ Branch 8 → 40 not taken.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 17 taken 179788 times.
|
179789 | if (entry.getQualType().is(TY_DYN)) |
| 238 |
3/6✓ Branch 11 → 12 taken 1 time.
✗ Branch 11 → 36 not taken.
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 34 not taken.
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 31 not taken.
|
1 | throw SemanticError(entry.declNode, UNEXPECTED_DYN_TYPE, "For the variable '" + name + "' no type could be inferred"); |
| 239 | |||
| 240 | // Check child scopes | ||
| 241 |
5/8✓ Branch 20 → 21 taken 79823 times.
✗ Branch 20 → 41 not taken.
✓ Branch 21 → 22 taken 79823 times.
✗ Branch 21 → 41 not taken.
✓ Branch 22 → 23 taken 79823 times.
✗ Branch 22 → 41 not taken.
✓ Branch 29 → 24 taken 77615 times.
✓ Branch 29 → 30 taken 79822 times.
|
157437 | for (const auto &scope : children | std::views::values) |
| 242 |
2/2✓ Branch 26 → 27 taken 77614 times.
✓ Branch 26 → 41 taken 1 time.
|
77615 | scope->ensureSuccessfulTypeInference(); |
| 243 | 79822 | } | |
| 244 | |||
| 245 | /** | ||
| 246 | * Get the number of fields if this is a struct scope | ||
| 247 | * | ||
| 248 | * @return Number of fields | ||
| 249 | */ | ||
| 250 | 48863 | size_t Scope::getFieldCount() const { | |
| 251 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 48863 times.
|
48863 | assert(type == ScopeType::STRUCT); |
| 252 | 48863 | size_t fieldCount = 0; | |
| 253 |
5/8✓ Branch 4 → 5 taken 48863 times.
✗ Branch 4 → 29 not taken.
✓ Branch 5 → 6 taken 48863 times.
✗ Branch 5 → 29 not taken.
✓ Branch 6 → 7 taken 48863 times.
✗ Branch 6 → 29 not taken.
✓ Branch 26 → 8 taken 908265 times.
✓ Branch 26 → 27 taken 48863 times.
|
957128 | for (const auto &symbol : symbolTable.symbols | std::views::values) { |
| 254 |
1/2✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 908265 times.
|
908265 | if (symbol.anonymous) |
| 255 | ✗ | continue; | |
| 256 |
1/2✓ Branch 11 → 12 taken 908265 times.
✗ Branch 11 → 29 not taken.
|
908265 | const QualType &symbolType = symbol.getQualType(); |
| 257 |
2/4✓ Branch 12 → 13 taken 908265 times.
✗ Branch 12 → 29 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 908265 times.
|
908265 | if (symbolType.is(TY_IMPORT)) |
| 258 | ✗ | continue; | |
| 259 | 908265 | const ASTNode *declNode = symbol.declNode; | |
| 260 |
8/10✓ Branch 15 → 16 taken 908265 times.
✗ Branch 15 → 29 not taken.
✓ Branch 16 → 17 taken 190102 times.
✓ Branch 16 → 19 taken 718163 times.
✓ Branch 17 → 18 taken 190102 times.
✗ Branch 17 → 29 not taken.
✓ Branch 18 → 19 taken 33420 times.
✓ Branch 18 → 20 taken 156682 times.
✓ Branch 21 → 22 taken 751583 times.
✓ Branch 21 → 23 taken 156682 times.
|
908265 | if (declNode->isFctOrProcDef() || declNode->isStructDef()) |
| 261 | 751583 | continue; | |
| 262 | 156682 | fieldCount++; | |
| 263 | } | ||
| 264 | 48863 | return fieldCount; | |
| 265 | } | ||
| 266 | |||
| 267 | /** | ||
| 268 | * Get all virtual methods in this scope, sorted by declaration code location | ||
| 269 | * | ||
| 270 | * @return List of virtual method pointers | ||
| 271 | */ | ||
| 272 | 766 | std::vector<Function *> Scope::getVirtualMethods() { | |
| 273 |
3/4✓ Branch 2 → 3 taken 348 times.
✓ Branch 2 → 5 taken 418 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 348 times.
|
766 | assert(type == ScopeType::STRUCT || type == ScopeType::INTERFACE); |
| 274 | |||
| 275 | // Collect all virtual methods | ||
| 276 | 766 | std::vector<Function *> methods; | |
| 277 |
2/2✓ Branch 37 → 7 taken 5040 times.
✓ Branch 37 → 38 taken 766 times.
|
5806 | for (auto &[fctId, manifestationList] : functions) { |
| 278 |
1/2✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 5040 times.
|
5040 | assert(!manifestationList.empty()); |
| 279 |
2/2✓ Branch 34 → 15 taken 7310 times.
✓ Branch 34 → 35 taken 5040 times.
|
12350 | for (auto &[mangledName, function] : manifestationList) |
| 280 |
2/2✓ Branch 27 → 28 taken 1730 times.
✓ Branch 27 → 32 taken 5580 times.
|
7310 | if (function.isVirtualMethod()) |
| 281 |
3/6✓ Branch 28 → 29 taken 1730 times.
✗ Branch 28 → 41 not taken.
✓ Branch 29 → 30 taken 1730 times.
✗ Branch 29 → 41 not taken.
✓ Branch 30 → 31 taken 1730 times.
✗ Branch 30 → 41 not taken.
|
1730 | methods.push_back(&functions.at(fctId).at(mangledName)); |
| 282 | } | ||
| 283 | |||
| 284 | // Sort the list | ||
| 285 |
2/4✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 2116 times.
✓ Branch 38 → 39 taken 766 times.
✗ Branch 38 → 44 not taken.
|
4998 | std::ranges::sort(methods, [](const Function *a, const Function *b) { return a->getDeclCodeLoc() < b->getDeclCodeLoc(); }); |
| 286 | |||
| 287 | 766 | return methods; | |
| 288 | ✗ | } | |
| 289 | |||
| 290 | /** | ||
| 291 | * Retrieve all struct manifestations in this scope in the order of their declaration | ||
| 292 | * | ||
| 293 | * @return All struct manifestations in declaration order | ||
| 294 | */ | ||
| 295 | 1161 | std::vector<const Struct *> Scope::getAllStructManifestationsInDeclarationOrder() const { | |
| 296 | // Retrieve all struct manifestations in this scope | ||
| 297 | 1161 | std::vector<const Struct *> manifestations; | |
| 298 |
1/2✓ Branch 3 → 4 taken 1161 times.
✗ Branch 3 → 27 not taken.
|
1161 | manifestations.reserve(structs.size()); // Reserve at least the size of individual generic structs |
| 299 |
5/8✓ Branch 4 → 5 taken 1161 times.
✗ Branch 4 → 26 not taken.
✓ Branch 5 → 6 taken 1161 times.
✗ Branch 5 → 26 not taken.
✓ Branch 6 → 7 taken 1161 times.
✗ Branch 6 → 26 not taken.
✓ Branch 20 → 8 taken 704 times.
✓ Branch 20 → 21 taken 1161 times.
|
1865 | for (const auto &structManifestations : structs | std::views::values) |
| 300 |
5/8✓ Branch 9 → 10 taken 704 times.
✗ Branch 9 → 25 not taken.
✓ Branch 10 → 11 taken 704 times.
✗ Branch 10 → 25 not taken.
✓ Branch 11 → 12 taken 704 times.
✗ Branch 11 → 25 not taken.
✓ Branch 17 → 13 taken 705 times.
✓ Branch 17 → 18 taken 704 times.
|
1409 | for (const auto &manifestation : structManifestations | std::views::values) |
| 301 |
1/2✓ Branch 14 → 15 taken 705 times.
✗ Branch 14 → 24 not taken.
|
705 | manifestations.push_back(&manifestation); |
| 302 | |||
| 303 | // Sort manifestations by declaration code location | ||
| 304 |
2/2✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 370 times.
|
744 | auto sortLambda = [](const Struct *lhs, const Struct *rhs) { return lhs->getDeclCodeLoc() < rhs->getDeclCodeLoc(); }; |
| 305 |
1/2✓ Branch 21 → 22 taken 1161 times.
✗ Branch 21 → 27 not taken.
|
1161 | std::ranges::sort(manifestations, sortLambda); |
| 306 | 1161 | return manifestations; | |
| 307 | ✗ | } | |
| 308 | |||
| 309 | /** | ||
| 310 | * Check if this struct has any reference fields | ||
| 311 | * | ||
| 312 | * @return Has reference fields or not | ||
| 313 | */ | ||
| 314 | 369 | bool Scope::hasRefFields() { | |
| 315 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 369 times.
|
369 | assert(type == ScopeType::STRUCT); |
| 316 |
2/2✓ Branch 16 → 5 taken 957 times.
✓ Branch 16 → 17 taken 357 times.
|
1314 | for (size_t i = 0; i < getFieldCount(); i++) |
| 317 |
3/4✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 957 times.
✓ Branch 12 → 13 taken 12 times.
✓ Branch 12 → 14 taken 945 times.
|
1914 | if (lookupField(i)->getQualType().isRef()) |
| 318 | 12 | return true; | |
| 319 | 357 | return false; | |
| 320 | } | ||
| 321 | |||
| 322 | /** | ||
| 323 | * Get the current number of nested loops | ||
| 324 | * | ||
| 325 | * @return Number of loops | ||
| 326 | */ | ||
| 327 | 2854 | unsigned int Scope::getLoopNestingDepth() const { // NOLINT(misc-no-recursion) | |
| 328 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 2854 times.
|
2854 | assert(!isRootScope()); |
| 329 |
2/2✓ Branch 6 → 7 taken 525 times.
✓ Branch 6 → 8 taken 2329 times.
|
2854 | if (parent->parent == nullptr) |
| 330 | 525 | return 0; | |
| 331 | 2329 | unsigned int loopCount = parent->getLoopNestingDepth(); | |
| 332 |
6/6✓ Branch 9 → 10 taken 2211 times.
✓ Branch 9 → 12 taken 118 times.
✓ Branch 10 → 11 taken 1425 times.
✓ Branch 10 → 12 taken 786 times.
✓ Branch 11 → 12 taken 24 times.
✓ Branch 11 → 13 taken 1401 times.
|
2329 | if (type == ScopeType::WHILE_BODY || type == ScopeType::FOR_BODY || type == ScopeType::FOREACH_BODY) |
| 333 | 928 | loopCount++; | |
| 334 | 2329 | return loopCount; | |
| 335 | } | ||
| 336 | |||
| 337 | /** | ||
| 338 | * Check if this scope is one of the child scopes of a switch statement | ||
| 339 | * | ||
| 340 | * @return Child scope of switch statement or not | ||
| 341 | */ | ||
| 342 | 7 | bool Scope::isInCaseBranch() const { // NOLINT(misc-no-recursion) | |
| 343 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 7 times.
|
7 | assert(!isRootScope()); |
| 344 |
2/2✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 8 taken 5 times.
|
7 | if (parent->parent == nullptr) |
| 345 | 2 | return false; | |
| 346 |
2/2✓ Branch 8 → 9 taken 4 times.
✓ Branch 8 → 10 taken 1 time.
|
5 | if (type == ScopeType::CASE_BODY) |
| 347 | 4 | return true; | |
| 348 | 1 | return parent->isInCaseBranch(); | |
| 349 | } | ||
| 350 | |||
| 351 | /** | ||
| 352 | * Check if this scope is within an async scope | ||
| 353 | * | ||
| 354 | * @return Within async scope or not | ||
| 355 | */ | ||
| 356 | 75 | bool Scope::isInAsyncScope() const { // NOLINT(misc-no-recursion) | |
| 357 |
2/2✓ Branch 2 → 3 taken 8 times.
✓ Branch 2 → 4 taken 67 times.
|
75 | if (isAsyncScope) |
| 358 | 8 | return true; | |
| 359 |
3/4✓ Branch 6 → 7 taken 43 times.
✓ Branch 6 → 10 taken 24 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 43 times.
|
67 | return !isRootScope() && parent->isInAsyncScope(); |
| 360 | } | ||
| 361 | |||
| 362 | /** | ||
| 363 | * Check if unsafe operations are allowed in this scope | ||
| 364 | * | ||
| 365 | * @return Allowed or not | ||
| 366 | */ | ||
| 367 | 5317 | bool Scope::doesAllowUnsafeOperations() const { // NOLINT(misc-no-recursion) | |
| 368 |
2/2✓ Branch 2 → 3 taken 3340 times.
✓ Branch 2 → 4 taken 1977 times.
|
5317 | if (type == ScopeType::UNSAFE_BODY) |
| 369 | 3340 | return true; | |
| 370 |
4/4✓ Branch 6 → 7 taken 1976 times.
✓ Branch 6 → 10 taken 1 time.
✓ Branch 8 → 9 taken 1975 times.
✓ Branch 8 → 10 taken 1 time.
|
1977 | return !isRootScope() && parent->doesAllowUnsafeOperations(); |
| 371 | } | ||
| 372 | |||
| 373 | /** | ||
| 374 | * Checks if this scope is imported | ||
| 375 | * | ||
| 376 | * @param askingScope Scope, which asks whether the current one is imported from its point of view or not | ||
| 377 | * | ||
| 378 | * @return Imported / not imported | ||
| 379 | */ | ||
| 380 | 99502 | bool Scope::isImportedBy(const Scope *askingScope) const { return askingScope->sourceFile->imports(sourceFile); } | |
| 381 | |||
| 382 | /** | ||
| 383 | * Get JSON representation of the symbol table | ||
| 384 | * | ||
| 385 | * @return Symbol table as JSON object | ||
| 386 | */ | ||
| 387 | 79888 | nlohmann::json Scope::getSymbolTableJSON() const { // NOLINT(misc-no-recursion) | |
| 388 |
1/2✓ Branch 2 → 3 taken 79888 times.
✗ Branch 2 → 42 not taken.
|
79888 | nlohmann::json result = symbolTable.toJSON(); |
| 389 | |||
| 390 | // Collect all children | ||
| 391 | 79888 | std::vector<nlohmann::json> jsonChildren; | |
| 392 |
1/2✓ Branch 4 → 5 taken 79888 times.
✗ Branch 4 → 38 not taken.
|
79888 | jsonChildren.reserve(children.size()); |
| 393 |
2/2✓ Branch 20 → 7 taken 77671 times.
✓ Branch 20 → 21 taken 79888 times.
|
157559 | for (const auto &[name, childScope] : children) { |
| 394 |
1/2✓ Branch 11 → 12 taken 77671 times.
✗ Branch 11 → 33 not taken.
|
77671 | nlohmann::json c = childScope->getSymbolTableJSON(); |
| 395 |
2/4✓ Branch 12 → 13 taken 77671 times.
✗ Branch 12 → 30 not taken.
✓ Branch 13 → 14 taken 77671 times.
✗ Branch 13 → 28 not taken.
|
77671 | c["name"] = name; // Inject symbol table name into JSON object |
| 396 |
1/2✓ Branch 16 → 17 taken 77671 times.
✗ Branch 16 → 31 not taken.
|
77671 | jsonChildren.emplace_back(c); |
| 397 | 77671 | } | |
| 398 |
2/4✓ Branch 21 → 22 taken 79888 times.
✗ Branch 21 → 37 not taken.
✓ Branch 22 → 23 taken 79888 times.
✗ Branch 22 → 35 not taken.
|
79888 | result["children"] = jsonChildren; |
| 399 | |||
| 400 | 79888 | return result; | |
| 401 | 79888 | } | |
| 402 | |||
| 403 | } // namespace spice::compiler | ||
| 404 |