| 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 | 22798 | Scope *Scope::createChildScope(const std::string &scopeName, ScopeType scopeType, const CodeLoc *declCodeLoc) { | |
| 21 |
3/6✓ Branch 2 → 3 taken 22798 times.
✗ Branch 2 → 14 not taken.
✓ Branch 3 → 4 taken 22798 times.
✗ Branch 3 → 12 not taken.
✓ Branch 4 → 5 taken 22798 times.
✗ Branch 4 → 10 not taken.
|
22798 | children.insert({scopeName, std::make_shared<Scope>(this, sourceFile, scopeType, declCodeLoc)}); |
| 22 | 22798 | 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 | 12230 | void Scope::renameChildScope(const std::string &oldName, const std::string &newName) { | |
| 33 |
4/8✓ Branch 2 → 3 taken 12230 times.
✗ Branch 2 → 19 not taken.
✓ Branch 3 → 4 taken 12230 times.
✗ Branch 3 → 7 not taken.
✓ Branch 4 → 5 taken 12230 times.
✗ Branch 4 → 19 not taken.
✓ Branch 5 → 6 taken 12230 times.
✗ Branch 5 → 7 not taken.
|
12230 | assert(children.contains(oldName) && !children.contains(newName)); |
| 34 |
1/2✓ Branch 8 → 9 taken 12230 times.
✗ Branch 8 → 19 not taken.
|
12230 | auto nodeHandler = children.extract(oldName); |
| 35 |
1/2✓ Branch 10 → 11 taken 12230 times.
✗ Branch 10 → 17 not taken.
|
12230 | nodeHandler.key() = newName; |
| 36 |
1/2✓ Branch 12 → 13 taken 12230 times.
✗ Branch 12 → 16 not taken.
|
12230 | children.insert(std::move(nodeHandler)); |
| 37 | 12230 | } | |
| 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 | 3620 | Scope *Scope::copyChildScope(const std::string &oldName, const std::string &newName) { | |
| 46 |
4/8✓ Branch 2 → 3 taken 3620 times.
✗ Branch 2 → 23 not taken.
✓ Branch 3 → 4 taken 3620 times.
✗ Branch 3 → 7 not taken.
✓ Branch 4 → 5 taken 3620 times.
✗ Branch 4 → 23 not taken.
✓ Branch 5 → 6 taken 3620 times.
✗ Branch 5 → 7 not taken.
|
3620 | assert(children.contains(oldName) && !children.contains(newName)); |
| 47 | // Create copy | ||
| 48 |
2/4✓ Branch 8 → 9 taken 3620 times.
✗ Branch 8 → 23 not taken.
✓ Branch 10 → 11 taken 3620 times.
✗ Branch 10 → 23 not taken.
|
3620 | const std::shared_ptr<Scope> newScope = children.at(oldName)->deepCopyScope(); |
| 49 | // Save copy under new name | ||
| 50 |
2/4✓ Branch 11 → 12 taken 3620 times.
✗ Branch 11 → 20 not taken.
✓ Branch 12 → 13 taken 3620 times.
✗ Branch 12 → 18 not taken.
|
3620 | children.insert({newName, newScope}); |
| 51 | 7240 | return newScope.get(); | |
| 52 | 3620 | } | |
| 53 | |||
| 54 | /** | ||
| 55 | * Deep copy the current scope and all its children | ||
| 56 | * | ||
| 57 | * @return Deep copy of the current scope | ||
| 58 | */ | ||
| 59 | 12867 | std::shared_ptr<Scope> Scope::deepCopyScope() { // NOLINT(misc-no-recursion) | |
| 60 | 12867 | const std::shared_ptr<Scope> newScope = std::make_shared<Scope>(*this); | |
| 61 |
2/2✓ Branch 30 → 5 taken 9247 times.
✓ Branch 30 → 31 taken 12867 times.
|
22114 | for (const auto &[childName, oldChild] : children) { |
| 62 |
2/4✓ Branch 9 → 10 taken 9247 times.
✗ Branch 9 → 37 not taken.
✓ Branch 11 → 12 taken 9247 times.
✗ Branch 11 → 35 not taken.
|
9247 | newScope->children[childName] = oldChild->deepCopyScope(); |
| 63 |
1/2✓ Branch 16 → 17 taken 9247 times.
✗ Branch 16 → 38 not taken.
|
9247 | newScope->children[childName]->parent = newScope.get(); |
| 64 |
2/4✓ Branch 19 → 20 taken 9247 times.
✗ Branch 19 → 38 not taken.
✓ Branch 22 → 23 taken 9247 times.
✗ Branch 22 → 38 not taken.
|
9247 | newScope->children[childName]->symbolTable.scope = newScope->children[childName].get(); |
| 65 |
1/2✓ Branch 26 → 27 taken 9247 times.
✗ Branch 26 → 38 not taken.
|
9247 | newScope->children[childName]->symbolTable.parent = &newScope->symbolTable; |
| 66 | } | ||
| 67 | 12867 | newScope->symbolTable.scope = newScope.get(); | |
| 68 | 12867 | 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 | 44781 | Scope *Scope::getChildScope(const std::string &scopeName) const { | |
| 78 |
5/6✓ Branch 3 → 4 taken 44780 times.
✓ Branch 3 → 7 taken 1 time.
✓ Branch 5 → 6 taken 44780 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 44780 times.
✓ Branch 8 → 11 taken 1 time.
|
44781 | if (!children.empty() && children.contains(scopeName)) |
| 79 | 44780 | 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 | 19217 | std::vector<SymbolTableEntry *> Scope::getVarsGoingOutOfScope() { // NOLINT(misc-no-recursion) | |
| 89 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 19217 times.
|
19217 | assert(!isRootScope()); // Should not be called in root scope |
| 90 | 19217 | std::vector<SymbolTableEntry *> varsGoingOutOfScope; | |
| 91 | |||
| 92 | // Collect all variables in this scope | ||
| 93 |
2/2✓ Branch 26 → 8 taken 32830 times.
✓ Branch 26 → 27 taken 19217 times.
|
52047 | for (const auto &[name, entry] : symbolTable.symbols) { |
| 94 | // Skip 'this' and result variables | ||
| 95 |
8/10✓ Branch 11 → 12 taken 32830 times.
✗ Branch 11 → 52 not taken.
✓ Branch 12 → 13 taken 26815 times.
✓ Branch 12 → 15 taken 6015 times.
✓ Branch 13 → 14 taken 26815 times.
✗ Branch 13 → 52 not taken.
✓ Branch 14 → 15 taken 7508 times.
✓ Branch 14 → 16 taken 19307 times.
✓ Branch 17 → 18 taken 13523 times.
✓ Branch 17 → 19 taken 19307 times.
|
32830 | if (name == THIS_VARIABLE_NAME || name == RETURN_VARIABLE_NAME) |
| 96 | 13523 | continue; | |
| 97 | // Skip parameters (ToDo: Remove when copy constructors work for by-value argument passing) | ||
| 98 |
2/2✓ Branch 19 → 20 taken 11764 times.
✓ Branch 19 → 21 taken 7543 times.
|
19307 | if (entry.isParam) |
| 99 | 11764 | continue; | |
| 100 | // Found variable, that goes out of scope | ||
| 101 |
2/4✓ Branch 21 → 22 taken 7543 times.
✗ Branch 21 → 51 not taken.
✓ Branch 22 → 23 taken 7543 times.
✗ Branch 22 → 51 not taken.
|
7543 | 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 107 times.
✓ Branch 27 → 49 taken 19110 times.
|
19217 | if (isDtorScope) { |
| 106 |
2/4✓ Branch 30 → 31 taken 107 times.
✗ Branch 30 → 33 not taken.
✓ Branch 31 → 32 taken 107 times.
✗ Branch 31 → 33 not taken.
|
107 | assert(!isRootScope() && parent->type == ScopeType::STRUCT); |
| 107 | // Get all fields of the struct | ||
| 108 |
2/2✓ Branch 47 → 36 taken 3680 times.
✓ Branch 47 → 48 taken 107 times.
|
3787 | for (const auto &[name, entry] : parent->symbolTable.symbols) |
| 109 |
4/6✓ Branch 39 → 40 taken 3680 times.
✗ Branch 39 → 55 not taken.
✓ Branch 40 → 41 taken 3680 times.
✗ Branch 40 → 53 not taken.
✓ Branch 41 → 42 taken 291 times.
✓ Branch 41 → 45 taken 3389 times.
|
3680 | if (!entry.getQualType().isOneOf({TY_FUNCTION, TY_PROCEDURE})) |
| 110 |
2/4✓ Branch 42 → 43 taken 291 times.
✗ Branch 42 → 54 not taken.
✓ Branch 43 → 44 taken 291 times.
✗ Branch 43 → 54 not taken.
|
291 | varsGoingOutOfScope.push_back(&parent->symbolTable.symbols.at(name)); |
| 111 | } | ||
| 112 | |||
| 113 | 19217 | 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 | 3448 | void Scope::insertGenericType(const std::string &typeName, const GenericType &genericType) { | |
| 123 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3448 times.
|
3448 | assert(!genericTypes.contains(typeName)); |
| 124 |
2/4✓ Branch 5 → 6 taken 3448 times.
✗ Branch 5 → 11 not taken.
✓ Branch 6 → 7 taken 3448 times.
✗ Branch 6 → 9 not taken.
|
3448 | genericTypes.insert({typeName, genericType}); |
| 125 | 3448 | } | |
| 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 | 31997 | GenericType *Scope::lookupGenericTypeStrict(const std::string &typeName) { | |
| 135 |
2/2✓ Branch 3 → 4 taken 12791 times.
✓ Branch 3 → 6 taken 19206 times.
|
31997 | 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 | 1433 | void Scope::collectWarnings(std::vector<CompilerWarning> &warnings) const { // NOLINT(misc-no-recursion) | |
| 145 | // Visit own symbols | ||
| 146 |
5/8✓ Branch 2 → 3 taken 1433 times.
✗ Branch 2 → 218 not taken.
✓ Branch 3 → 4 taken 1433 times.
✗ Branch 3 → 218 not taken.
✓ Branch 4 → 5 taken 1433 times.
✗ Branch 4 → 218 not taken.
✓ Branch 146 → 6 taken 3047 times.
✓ Branch 146 → 147 taken 1433 times.
|
4480 | for (const auto &entry : symbolTable.symbols | std::views::values) { |
| 147 | // Do not produce a warning if the symbol is used or has a special name | ||
| 148 | 3047 | const std::string &name = entry.name; | |
| 149 |
6/6✓ Branch 7 → 8 taken 235 times.
✓ Branch 7 → 10 taken 2812 times.
✓ Branch 9 → 10 taken 60 times.
✓ Branch 9 → 11 taken 175 times.
✓ Branch 12 → 13 taken 2872 times.
✓ Branch 12 → 14 taken 175 times.
|
3047 | if (entry.used || name.starts_with(UNUSED_VARIABLE_NAME)) |
| 150 | 2896 | continue; | |
| 151 | |||
| 152 |
1/2✓ Branch 14 → 15 taken 175 times.
✗ Branch 14 → 217 not taken.
|
175 | const QualType entryType = entry.getQualType(); |
| 153 | |||
| 154 | // Determine warning type and message by the scope type and the symbol type | ||
| 155 | 175 | CompilerWarningType warningType = UNUSED_VARIABLE; | |
| 156 | 175 | std::string warningMessage; | |
| 157 |
4/5✓ Branch 16 → 17 taken 29 times.
✓ Branch 16 → 92 taken 52 times.
✓ Branch 16 → 121 taken 37 times.
✗ Branch 16 → 127 not taken.
✓ Branch 16 → 130 taken 57 times.
|
175 | switch (type) { |
| 158 | 29 | case ScopeType::GLOBAL: { | |
| 159 | // Skip generic function/procedure/struct/interface entries | ||
| 160 |
6/10✓ Branch 17 → 18 taken 29 times.
✗ Branch 17 → 160 not taken.
✓ Branch 18 → 19 taken 17 times.
✓ Branch 18 → 23 taken 12 times.
✓ Branch 19 → 20 taken 17 times.
✗ Branch 19 → 160 not taken.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 17 times.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 29 times.
|
29 | if (entryType.isOneOf({TY_FUNCTION, TY_PROCEDURE, TY_STRUCT, TY_INTERFACE}) && !entryType.getTemplateTypes().empty()) |
| 161 | ✗ | continue; | |
| 162 | |||
| 163 |
3/4✓ Branch 26 → 27 taken 29 times.
✗ Branch 26 → 215 not taken.
✓ Branch 27 → 28 taken 11 times.
✓ Branch 27 → 38 taken 18 times.
|
29 | if (entryType.is(TY_FUNCTION)) { |
| 164 |
1/2✓ Branch 28 → 29 taken 11 times.
✗ Branch 28 → 215 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 → 165 not taken.
✓ Branch 31 → 32 taken 11 times.
✗ Branch 31 → 163 not taken.
✓ Branch 32 → 33 taken 11 times.
✗ Branch 32 → 161 not taken.
|
11 | warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused"; |
| 167 |
3/4✓ Branch 38 → 39 taken 18 times.
✗ Branch 38 → 215 not taken.
✓ Branch 39 → 40 taken 3 times.
✓ Branch 39 → 50 taken 15 times.
|
18 | } else if (entryType.is(TY_PROCEDURE)) { |
| 168 |
1/2✓ Branch 40 → 41 taken 3 times.
✗ Branch 40 → 215 not taken.
|
3 | const std::vector<Function *> *fctManifestations = entry.declNode->getFctManifestations(name); |
| 169 | 3 | warningType = UNUSED_PROCEDURE; | |
| 170 |
3/6✓ Branch 42 → 43 taken 3 times.
✗ Branch 42 → 172 not taken.
✓ Branch 43 → 44 taken 3 times.
✗ Branch 43 → 170 not taken.
✓ Branch 44 → 45 taken 3 times.
✗ Branch 44 → 168 not taken.
|
3 | warningMessage = "'" + fctManifestations->front()->getSignature() + "' is unused"; |
| 171 |
3/4✓ Branch 50 → 51 taken 15 times.
✗ Branch 50 → 215 not taken.
✓ Branch 51 → 52 taken 2 times.
✓ Branch 51 → 58 taken 13 times.
|
15 | } else if (entryType.is(TY_STRUCT)) { |
| 172 | 2 | warningType = UNUSED_STRUCT; | |
| 173 |
2/4✓ Branch 52 → 53 taken 2 times.
✗ Branch 52 → 177 not taken.
✓ Branch 53 → 54 taken 2 times.
✗ Branch 53 → 175 not taken.
|
2 | warningMessage = "The struct '" + entry.name + "' is unused"; |
| 174 |
3/4✓ Branch 58 → 59 taken 13 times.
✗ Branch 58 → 215 not taken.
✓ Branch 59 → 60 taken 1 time.
✓ Branch 59 → 66 taken 12 times.
|
13 | } else if (entryType.is(TY_INTERFACE)) { |
| 175 | 1 | warningType = UNUSED_INTERFACE; | |
| 176 |
2/4✓ Branch 60 → 61 taken 1 time.
✗ Branch 60 → 181 not taken.
✓ Branch 61 → 62 taken 1 time.
✗ Branch 61 → 179 not taken.
|
1 | warningMessage = "The interface '" + entry.name + "' is unused"; |
| 177 |
3/4✓ Branch 66 → 67 taken 12 times.
✗ Branch 66 → 215 not taken.
✓ Branch 67 → 68 taken 2 times.
✓ Branch 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 69 → 70 taken 10 times.
✗ Branch 69 → 215 not taken.
✓ Branch 70 → 71 taken 4 times.
✓ Branch 70 → 77 taken 6 times.
|
10 | } else if (entryType.is(TY_IMPORT)) { |
| 180 | 4 | warningType = UNUSED_IMPORT; | |
| 181 |
2/4✓ Branch 71 → 72 taken 4 times.
✗ Branch 71 → 185 not taken.
✓ Branch 72 → 73 taken 4 times.
✗ Branch 72 → 183 not taken.
|
4 | warningMessage = "The import '" + entry.name + "' is unused"; |
| 182 |
3/4✓ Branch 77 → 78 taken 6 times.
✗ Branch 77 → 215 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 → 189 not taken.
✓ Branch 80 → 81 taken 1 time.
✗ Branch 80 → 187 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 → 193 not taken.
✓ Branch 86 → 87 taken 5 times.
✗ Branch 86 → 191 not taken.
|
5 | warningMessage = "The variable '" + entry.name + "' is unused"; |
| 188 | } | ||
| 189 | |||
| 190 | 27 | break; | |
| 191 | } | ||
| 192 | 52 | case ScopeType::STRUCT: // fall-through | |
| 193 | case ScopeType::INTERFACE: { | ||
| 194 |
3/4✓ Branch 92 → 93 taken 52 times.
✗ Branch 92 → 215 not taken.
✓ Branch 93 → 94 taken 11 times.
✓ Branch 93 → 100 taken 41 times.
|
52 | if (entry.isField()) { |
| 195 | 11 | warningType = UNUSED_FIELD; | |
| 196 |
2/4✓ Branch 94 → 95 taken 11 times.
✗ Branch 94 → 197 not taken.
✓ Branch 95 → 96 taken 11 times.
✗ Branch 95 → 195 not taken.
|
11 | warningMessage = "The field '" + entry.name + "' is unused"; |
| 197 |
2/4✓ Branch 100 → 101 taken 41 times.
✗ Branch 100 → 199 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 → 215 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 → 204 not taken.
✓ Branch 113 → 114 taken 19 times.
✗ Branch 113 → 202 not taken.
✓ Branch 114 → 115 taken 19 times.
✗ Branch 114 → 200 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 121 → 122 taken 37 times.
✗ Branch 121 → 209 not taken.
✓ Branch 122 → 123 taken 37 times.
✗ Branch 122 → 207 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 | 57 | warningType = UNUSED_VARIABLE; | |
| 221 |
2/4✓ Branch 130 → 131 taken 57 times.
✗ Branch 130 → 213 not taken.
✓ Branch 131 → 132 taken 57 times.
✗ Branch 131 → 211 not taken.
|
57 | warningMessage = "The variable '" + entry.name + "' is unused"; |
| 222 | 57 | break; | |
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | // Add warning | ||
| 227 |
2/4✓ Branch 136 → 137 taken 151 times.
✗ Branch 136 → 215 not taken.
✓ Branch 137 → 138 taken 151 times.
✗ Branch 137 → 215 not taken.
|
151 | warnings.emplace_back(entry.getDeclCodeLoc(), warningType, warningMessage); |
| 228 |
2/2✓ Branch 140 → 141 taken 151 times.
✓ Branch 140 → 143 taken 24 times.
|
175 | } |
| 229 | |||
| 230 | // Visit children | ||
| 231 |
5/8✓ Branch 147 → 148 taken 1433 times.
✗ Branch 147 → 219 not taken.
✓ Branch 148 → 149 taken 1433 times.
✗ Branch 148 → 219 not taken.
✓ Branch 149 → 150 taken 1433 times.
✗ Branch 149 → 219 not taken.
✓ Branch 158 → 151 taken 1201 times.
✓ Branch 158 → 159 taken 1433 times.
|
2634 | for (const auto &childScope : children | std::views::values) |
| 232 |
2/2✓ Branch 153 → 154 taken 1163 times.
✓ Branch 153 → 156 taken 38 times.
|
1201 | if (!childScope->isGenericScope) |
| 233 |
1/2✓ Branch 155 → 156 taken 1163 times.
✗ Branch 155 → 219 not taken.
|
1163 | childScope->collectWarnings(warnings); |
| 234 | 1433 | } | |
| 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 | 74192 | void Scope::ensureSuccessfulTypeInference() const { // NOLINT(misc-no-recursion) | |
| 241 | // Check symbols in this scope | ||
| 242 |
2/2✓ Branch 19 → 4 taken 165271 times.
✓ Branch 19 → 20 taken 74191 times.
|
239462 | for (auto &[name, entry] : symbolTable.symbols) |
| 243 |
4/6✓ Branch 7 → 8 taken 165271 times.
✗ Branch 7 → 40 not taken.
✓ Branch 8 → 9 taken 165271 times.
✗ Branch 8 → 40 not taken.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 17 taken 165270 times.
|
165271 | if (entry.getQualType().is(TY_DYN)) |
| 244 |
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"); |
| 245 | |||
| 246 | // Check child scopes | ||
| 247 |
5/8✓ Branch 20 → 21 taken 74191 times.
✗ Branch 20 → 41 not taken.
✓ Branch 21 → 22 taken 74191 times.
✗ Branch 21 → 41 not taken.
✓ Branch 22 → 23 taken 74191 times.
✗ Branch 22 → 41 not taken.
✓ Branch 29 → 24 taken 72118 times.
✓ Branch 29 → 30 taken 74190 times.
|
146308 | for (const auto &scope : children | std::views::values) |
| 248 |
2/2✓ Branch 26 → 27 taken 72117 times.
✓ Branch 26 → 41 taken 1 time.
|
72118 | scope->ensureSuccessfulTypeInference(); |
| 249 | 74190 | } | |
| 250 | |||
| 251 | /** | ||
| 252 | * Get the number of fields if this is a struct scope | ||
| 253 | * | ||
| 254 | * @return Number of fields | ||
| 255 | */ | ||
| 256 | 46393 | size_t Scope::getFieldCount() const { | |
| 257 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 46393 times.
|
46393 | assert(type == ScopeType::STRUCT); |
| 258 | 46393 | size_t fieldCount = 0; | |
| 259 |
5/8✓ Branch 4 → 5 taken 46393 times.
✗ Branch 4 → 29 not taken.
✓ Branch 5 → 6 taken 46393 times.
✗ Branch 5 → 29 not taken.
✓ Branch 6 → 7 taken 46393 times.
✗ Branch 6 → 29 not taken.
✓ Branch 26 → 8 taken 863348 times.
✓ Branch 26 → 27 taken 46393 times.
|
909741 | for (const auto &symbol : symbolTable.symbols | std::views::values) { |
| 260 |
1/2✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 863348 times.
|
863348 | if (symbol.anonymous) |
| 261 | ✗ | continue; | |
| 262 |
1/2✓ Branch 11 → 12 taken 863348 times.
✗ Branch 11 → 29 not taken.
|
863348 | const QualType &symbolType = symbol.getQualType(); |
| 263 |
2/4✓ Branch 12 → 13 taken 863348 times.
✗ Branch 12 → 29 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 863348 times.
|
863348 | if (symbolType.is(TY_IMPORT)) |
| 264 | ✗ | continue; | |
| 265 | 863348 | const ASTNode *declNode = symbol.declNode; | |
| 266 |
8/10✓ Branch 15 → 16 taken 863348 times.
✗ Branch 15 → 29 not taken.
✓ Branch 16 → 17 taken 182540 times.
✓ Branch 16 → 19 taken 680808 times.
✓ Branch 17 → 18 taken 182540 times.
✗ Branch 17 → 29 not taken.
✓ Branch 18 → 19 taken 32732 times.
✓ Branch 18 → 20 taken 149808 times.
✓ Branch 21 → 22 taken 713540 times.
✓ Branch 21 → 23 taken 149808 times.
|
863348 | if (declNode->isFctOrProcDef() || declNode->isStructDef()) |
| 267 | 713540 | continue; | |
| 268 | 149808 | fieldCount++; | |
| 269 | } | ||
| 270 | 46393 | 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 | 710 | std::vector<Function *> Scope::getVirtualMethods() { | |
| 279 |
3/4✓ Branch 2 → 3 taken 324 times.
✓ Branch 2 → 5 taken 386 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 324 times.
|
710 | assert(type == ScopeType::STRUCT || type == ScopeType::INTERFACE); |
| 280 | |||
| 281 | // Collect all virtual methods | ||
| 282 | 710 | std::vector<Function *> methods; | |
| 283 |
2/2✓ Branch 37 → 7 taken 4608 times.
✓ Branch 37 → 38 taken 710 times.
|
5318 | for (auto &[fctId, manifestationList] : functions) { |
| 284 |
1/2✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 4608 times.
|
4608 | assert(!manifestationList.empty()); |
| 285 |
2/2✓ Branch 34 → 15 taken 6726 times.
✓ Branch 34 → 35 taken 4608 times.
|
11334 | for (auto &[mangledName, function] : manifestationList) |
| 286 |
2/2✓ Branch 27 → 28 taken 1610 times.
✓ Branch 27 → 32 taken 5116 times.
|
6726 | if (function.isVirtualMethod()) |
| 287 |
3/6✓ Branch 28 → 29 taken 1610 times.
✗ Branch 28 → 41 not taken.
✓ Branch 29 → 30 taken 1610 times.
✗ Branch 29 → 41 not taken.
✓ Branch 30 → 31 taken 1610 times.
✗ Branch 30 → 41 not taken.
|
1610 | methods.push_back(&functions.at(fctId).at(mangledName)); |
| 288 | } | ||
| 289 | |||
| 290 | // Sort the list | ||
| 291 |
2/4✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1972 times.
✓ Branch 38 → 39 taken 710 times.
✗ Branch 38 → 44 not taken.
|
4654 | std::ranges::sort(methods, [](const Function *a, const Function *b) { return a->getDeclCodeLoc() < b->getDeclCodeLoc(); }); |
| 292 | |||
| 293 | 710 | 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 | 1084 | std::vector<const Struct *> Scope::getAllStructManifestationsInDeclarationOrder() const { | |
| 302 | // Retrieve all struct manifestations in this scope | ||
| 303 | 1084 | std::vector<const Struct *> manifestations; | |
| 304 |
1/2✓ Branch 3 → 4 taken 1084 times.
✗ Branch 3 → 27 not taken.
|
1084 | manifestations.reserve(structs.size()); // Reserve at least the size of individual generic structs |
| 305 |
5/8✓ Branch 4 → 5 taken 1084 times.
✗ Branch 4 → 26 not taken.
✓ Branch 5 → 6 taken 1084 times.
✗ Branch 5 → 26 not taken.
✓ Branch 6 → 7 taken 1084 times.
✗ Branch 6 → 26 not taken.
✓ Branch 20 → 8 taken 664 times.
✓ Branch 20 → 21 taken 1084 times.
|
1748 | for (const auto &structManifestations : structs | std::views::values) |
| 306 |
5/8✓ Branch 9 → 10 taken 664 times.
✗ Branch 9 → 25 not taken.
✓ Branch 10 → 11 taken 664 times.
✗ Branch 10 → 25 not taken.
✓ Branch 11 → 12 taken 664 times.
✗ Branch 11 → 25 not taken.
✓ Branch 17 → 13 taken 665 times.
✓ Branch 17 → 18 taken 664 times.
|
1329 | for (const auto &manifestation : structManifestations | std::views::values) |
| 307 |
1/2✓ Branch 14 → 15 taken 665 times.
✗ Branch 14 → 24 not taken.
|
665 | manifestations.push_back(&manifestation); |
| 308 | |||
| 309 | // Sort manifestations by declaration code location | ||
| 310 |
2/2✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 334 times.
|
672 | auto sortLambda = [](const Struct *lhs, const Struct *rhs) { return lhs->getDeclCodeLoc() < rhs->getDeclCodeLoc(); }; |
| 311 |
1/2✓ Branch 21 → 22 taken 1084 times.
✗ Branch 21 → 27 not taken.
|
1084 | std::ranges::sort(manifestations, sortLambda); |
| 312 | 1084 | return manifestations; | |
| 313 | ✗ | } | |
| 314 | |||
| 315 | /** | ||
| 316 | * Check if this struct has any reference fields | ||
| 317 | * | ||
| 318 | * @return Has reference fields or not | ||
| 319 | */ | ||
| 320 | 352 | bool Scope::hasRefFields() { | |
| 321 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 352 times.
|
352 | assert(type == ScopeType::STRUCT); |
| 322 |
2/2✓ Branch 16 → 5 taken 909 times.
✓ Branch 16 → 17 taken 340 times.
|
1249 | for (size_t i = 0; i < getFieldCount(); i++) |
| 323 |
3/4✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 909 times.
✓ Branch 12 → 13 taken 12 times.
✓ Branch 12 → 14 taken 897 times.
|
1818 | if (lookupField(i)->getQualType().isRef()) |
| 324 | 12 | return true; | |
| 325 | 340 | return false; | |
| 326 | } | ||
| 327 | |||
| 328 | /** | ||
| 329 | * Get the current number of nested loops | ||
| 330 | * | ||
| 331 | * @return Number of loops | ||
| 332 | */ | ||
| 333 | 2656 | unsigned int Scope::getLoopNestingDepth() const { // NOLINT(misc-no-recursion) | |
| 334 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 2656 times.
|
2656 | assert(!isRootScope()); |
| 335 |
2/2✓ Branch 6 → 7 taken 485 times.
✓ Branch 6 → 8 taken 2171 times.
|
2656 | if (parent->parent == nullptr) |
| 336 | 485 | return 0; | |
| 337 | 2171 | unsigned int loopCount = parent->getLoopNestingDepth(); | |
| 338 |
6/6✓ Branch 9 → 10 taken 2061 times.
✓ Branch 9 → 12 taken 110 times.
✓ Branch 10 → 11 taken 1331 times.
✓ Branch 10 → 12 taken 730 times.
✓ Branch 11 → 12 taken 24 times.
✓ Branch 11 → 13 taken 1307 times.
|
2171 | if (type == ScopeType::WHILE_BODY || type == ScopeType::FOR_BODY || type == ScopeType::FOREACH_BODY) |
| 339 | 864 | loopCount++; | |
| 340 | 2171 | 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 4 → 5 not taken.
✓ Branch 4 → 6 taken 7 times.
|
7 | assert(!isRootScope()); |
| 350 |
2/2✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 8 taken 5 times.
|
7 | if (parent->parent == nullptr) |
| 351 | 2 | return false; | |
| 352 |
2/2✓ Branch 8 → 9 taken 4 times.
✓ Branch 8 → 10 taken 1 time.
|
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 2 → 3 taken 8 times.
✓ Branch 2 → 4 taken 67 times.
|
75 | if (isAsyncScope) |
| 364 | 8 | return true; | |
| 365 |
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(); |
| 366 | } | ||
| 367 | |||
| 368 | /** | ||
| 369 | * Check if unsafe operations are allowed in this scope | ||
| 370 | * | ||
| 371 | * @return Allowed or not | ||
| 372 | */ | ||
| 373 | 4959 | bool Scope::doesAllowUnsafeOperations() const { // NOLINT(misc-no-recursion) | |
| 374 |
2/2✓ Branch 2 → 3 taken 3108 times.
✓ Branch 2 → 4 taken 1851 times.
|
4959 | if (type == ScopeType::UNSAFE_BODY) |
| 375 | 3108 | return true; | |
| 376 |
4/4✓ Branch 6 → 7 taken 1850 times.
✓ Branch 6 → 10 taken 1 time.
✓ Branch 8 → 9 taken 1849 times.
✓ Branch 8 → 10 taken 1 time.
|
1851 | 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 | 87954 | 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 | 74256 | nlohmann::json Scope::getSymbolTableJSON() const { // NOLINT(misc-no-recursion) | |
| 394 |
1/2✓ Branch 2 → 3 taken 74256 times.
✗ Branch 2 → 42 not taken.
|
74256 | nlohmann::json result = symbolTable.toJSON(); |
| 395 | |||
| 396 | // Collect all children | ||
| 397 | 74256 | std::vector<nlohmann::json> jsonChildren; | |
| 398 |
1/2✓ Branch 4 → 5 taken 74256 times.
✗ Branch 4 → 38 not taken.
|
74256 | jsonChildren.reserve(children.size()); |
| 399 |
2/2✓ Branch 20 → 7 taken 72174 times.
✓ Branch 20 → 21 taken 74256 times.
|
146430 | for (const auto &[name, childScope] : children) { |
| 400 |
1/2✓ Branch 11 → 12 taken 72174 times.
✗ Branch 11 → 33 not taken.
|
72174 | nlohmann::json c = childScope->getSymbolTableJSON(); |
| 401 |
2/4✓ Branch 12 → 13 taken 72174 times.
✗ Branch 12 → 30 not taken.
✓ Branch 13 → 14 taken 72174 times.
✗ Branch 13 → 28 not taken.
|
72174 | c["name"] = name; // Inject symbol table name into JSON object |
| 402 |
1/2✓ Branch 16 → 17 taken 72174 times.
✗ Branch 16 → 31 not taken.
|
72174 | jsonChildren.emplace_back(c); |
| 403 | 72174 | } | |
| 404 |
2/4✓ Branch 21 → 22 taken 74256 times.
✗ Branch 21 → 37 not taken.
✓ Branch 22 → 23 taken 74256 times.
✗ Branch 22 → 35 not taken.
|
74256 | result["children"] = jsonChildren; |
| 405 | |||
| 406 | 74256 | return result; | |
| 407 | 74256 | } | |
| 408 | |||
| 409 | } // namespace spice::compiler | ||
| 410 |