GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 58.8% 204 / 0 / 347
Functions: 81.7% 58 / 0 / 71
Branches: 38.1% 209 / 0 / 548

src/ast/ASTNodes.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 #include <ast/ASTNodes.h>
4
5 #include <SourceFile.h>
6 #include <ast/Attributes.h>
7 #include <exception/SemanticError.h>
8 #include <symboltablebuilder/SymbolTableBuilder.h>
9 #include <typechecker/Builtins.h>
10
11 namespace spice::compiler {
12
13 // Constant definitions
14 static constexpr size_t ERROR_MESSAGE_CONTEXT = 20;
15
16 2464 std::string ASTNode::getErrorMessage() const {
17 2464 antlr4::CharStream *inputStream = codeLoc.sourceFile->antlrCtx.inputStream.get();
18 2464 const antlr4::misc::Interval &sourceInterval = codeLoc.sourceInterval;
19 2464 antlr4::misc::Interval extSourceInterval(sourceInterval);
20
21 // If we have a multi-line interval, only use the first line
22
3/4
✓ Branch 3 → 4 taken 2464 times.
✗ Branch 3 → 77 not taken.
✓ Branch 6 → 7 taken 19 times.
✓ Branch 6 → 8 taken 2445 times.
2464 if (const size_t offset = inputStream->getText(extSourceInterval).find('\n'); offset != std::string::npos)
23 19 extSourceInterval.b = extSourceInterval.a + static_cast<ssize_t>(offset);
24
25 2464 size_t markerIndentation = 0;
26
2/2
✓ Branch 20 → 9 taken 29476 times.
✓ Branch 20 → 21 taken 748 times.
30224 for (; markerIndentation < ERROR_MESSAGE_CONTEXT; markerIndentation++) {
27 29476 extSourceInterval.a--;
28
9/12
✓ Branch 9 → 10 taken 29462 times.
✓ Branch 9 → 13 taken 14 times.
✓ Branch 10 → 11 taken 29462 times.
✗ Branch 10 → 78 not taken.
✓ Branch 12 → 13 taken 1702 times.
✓ Branch 12 → 14 taken 27760 times.
✓ Branch 15 → 16 taken 29462 times.
✓ Branch 15 → 17 taken 14 times.
✓ Branch 17 → 18 taken 1716 times.
✓ Branch 17 → 19 taken 27760 times.
✗ Branch 78 → 79 not taken.
✗ Branch 78 → 80 not taken.
29476 if (extSourceInterval.a < 0 || inputStream->getText(extSourceInterval).find('\n') != std::string::npos) {
29 1716 extSourceInterval.a++;
30 1716 break;
31 }
32 }
33
2/2
✓ Branch 34 → 22 taken 7880 times.
✓ Branch 34 → 35 taken 57 times.
7937 for (size_t suffixContext = 0; suffixContext < ERROR_MESSAGE_CONTEXT; suffixContext++) {
34 7880 extSourceInterval.b++;
35
4/6
✓ Branch 22 → 23 taken 7880 times.
✗ Branch 22 → 82 not taken.
✓ Branch 23 → 24 taken 7880 times.
✗ Branch 23 → 27 not taken.
✓ Branch 26 → 27 taken 2407 times.
✓ Branch 26 → 28 taken 5473 times.
15760 if (static_cast<size_t>(extSourceInterval.b) > inputStream->size() ||
36
4/8
✓ Branch 24 → 25 taken 7880 times.
✗ Branch 24 → 82 not taken.
✓ Branch 29 → 30 taken 7880 times.
✗ Branch 29 → 31 not taken.
✓ Branch 31 → 32 taken 2407 times.
✓ Branch 31 → 33 taken 5473 times.
✗ Branch 82 → 83 not taken.
✗ Branch 82 → 84 not taken.
15760 inputStream->getText(extSourceInterval).find('\n') != std::string::npos) {
37 2407 extSourceInterval.b--;
38 2407 break;
39 }
40 }
41
42 // Trim start
43
3/4
✓ Branch 37 → 38 taken 14927 times.
✗ Branch 37 → 86 not taken.
✓ Branch 40 → 36 taken 12463 times.
✓ Branch 40 → 41 taken 2464 times.
14927 while (inputStream->getText(extSourceInterval)[0] == ' ') {
44 12463 extSourceInterval.a++;
45 12463 markerIndentation--;
46 }
47
48 // Trim end
49
3/4
✓ Branch 41 → 42 taken 2464 times.
✗ Branch 41 → 87 not taken.
✓ Branch 45 → 46 taken 19 times.
✓ Branch 45 → 47 taken 2445 times.
2464 if (inputStream->getText(extSourceInterval)[extSourceInterval.length() - 1] == '\n')
50 19 extSourceInterval.b--;
51
52 2464 const std::string lineNumberStr = std::to_string(codeLoc.line);
53 2464 markerIndentation += lineNumberStr.length() + 2;
54
55 // Build error message
56
1/2
✓ Branch 49 → 50 taken 2464 times.
✗ Branch 49 → 107 not taken.
2464 std::stringstream ss;
57
5/10
✓ Branch 50 → 51 taken 2464 times.
✗ Branch 50 → 105 not taken.
✓ Branch 51 → 52 taken 2464 times.
✗ Branch 51 → 105 not taken.
✓ Branch 52 → 53 taken 2464 times.
✗ Branch 52 → 90 not taken.
✓ Branch 53 → 54 taken 2464 times.
✗ Branch 53 → 88 not taken.
✓ Branch 54 → 55 taken 2464 times.
✗ Branch 54 → 88 not taken.
2464 ss << lineNumberStr << " " << inputStream->getText(extSourceInterval) << "\n";
58
2/4
✓ Branch 58 → 59 taken 2464 times.
✗ Branch 58 → 93 not taken.
✓ Branch 59 → 60 taken 2464 times.
✗ Branch 59 → 91 not taken.
4928 ss << std::string(markerIndentation, ' ');
59
2/4
✓ Branch 67 → 68 taken 2464 times.
✗ Branch 67 → 99 not taken.
✓ Branch 68 → 69 taken 2464 times.
✗ Branch 68 → 97 not taken.
2464 ss << std::string(std::min(sourceInterval.length(), extSourceInterval.length()), '^');
60
1/2
✓ Branch 71 → 72 taken 2464 times.
✗ Branch 71 → 105 not taken.
4928 return ss.str();
61 2464 }
62
63 67811 const StmtLstNode *ASTNode::getNextOuterStmtLst() const { // NOLINT(*-no-recursion)
64
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 67811 times.
67811 assert(parent != nullptr);
65
2/2
✓ Branch 5 → 6 taken 58995 times.
✓ Branch 5 → 14 taken 8816 times.
126806 return isStmtLst() ? spice_pointer_cast<const StmtLstNode *>(this) : parent->getNextOuterStmtLst();
66 }
67
68 496 bool MainFctDefNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
69 496 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
70 }
71
72 30350 bool FctDefBaseNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
73 30350 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
74 }
75
76 175 CompileTimeValue GlobalVarDefNode::getCompileTimeValue(size_t manIdx) const { return constant->getCompileTimeValue(manIdx); }
77
78 2452 bool ForLoopNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
79 // If we have the guarantee that the loop condition is always true and the loop body returns on all control paths,
80 // we can assume that the loop itself will always return
81
3/4
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 7 taken 2451 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 1 time.
2452 const bool condAlwaysTrue = condAssign->hasCompileTimeValue(manIdx) && condAssign->getCompileTimeValue(manIdx).boolValue;
82
1/4
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 12 taken 2452 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
2452 return condAlwaysTrue && body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
83 }
84
85 1586 bool WhileLoopNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
86 // If we have the guarantee that the loop condition is always true and the loop body returns on all control paths,
87 // we can assume that the loop itself will always return
88
3/4
✓ Branch 3 → 4 taken 48 times.
✓ Branch 3 → 7 taken 1538 times.
✓ Branch 5 → 6 taken 48 times.
✗ Branch 5 → 7 not taken.
1586 const bool condAlwaysTrue = condition->hasCompileTimeValue(manIdx) && condition->getCompileTimeValue(manIdx).boolValue;
89
4/4
✓ Branch 8 → 9 taken 48 times.
✓ Branch 8 → 12 taken 1538 times.
✓ Branch 10 → 11 taken 8 times.
✓ Branch 10 → 12 taken 40 times.
1586 return condAlwaysTrue && body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
90 }
91
92 16 bool DoWhileLoopNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
93 // Do-while loops will always be executed at least once. So if the body returns on all control paths, the loop will as well
94 16 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
95 }
96
97 8820 bool IfStmtNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const { // NOLINT(misc-no-recursion)
98 // If the condition always evaluates to 'true' the then block must return
99
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 8820 times.
8820 if (!doCompileElseBranch(manIdx))
100 return thenBody->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
101
102 // If the condition always evaluates to 'false' the else block must return
103
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 14 taken 8820 times.
8820 if (!doCompileThenBranch(manIdx))
104 return elseStmt != nullptr && elseStmt->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
105
106 // If the condition does not always evaluate to 'true' or 'false' we need to check both branches
107
4/4
✓ Branch 15 → 16 taken 6202 times.
✓ Branch 15 → 20 taken 2618 times.
✓ Branch 16 → 17 taken 130 times.
✓ Branch 16 → 20 taken 6072 times.
8950 return thenBody->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx) && elseStmt != nullptr &&
108
2/2
✓ Branch 18 → 19 taken 117 times.
✓ Branch 18 → 20 taken 13 times.
8950 elseStmt->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
109 }
110
111 130 bool ElseStmtNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable,
112 size_t manIdx) const { // NOLINT(misc-no-recursion)
113
2/2
✓ Branch 2 → 3 taken 43 times.
✓ Branch 2 → 5 taken 87 times.
130 if (isElseIf)
114 43 return ifStmt->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
115 87 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
116 }
117
118 85 bool SwitchStmtNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
119 530 const auto pred = [=](const CaseBranchNode *node) {
120 445 return node->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
121 85 };
122
1/2
✓ Branch 2 → 3 taken 85 times.
✗ Branch 2 → 14 not taken.
85 const bool allCaseBranchesReturn = std::ranges::all_of(caseBranches, pred);
123 const bool defaultBranchReturns =
124
5/6
✓ Branch 3 → 4 taken 69 times.
✓ Branch 3 → 6 taken 16 times.
✓ Branch 4 → 5 taken 69 times.
✗ Branch 4 → 14 not taken.
✓ Branch 5 → 6 taken 61 times.
✓ Branch 5 → 7 taken 8 times.
85 !defaultBranch || defaultBranch->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
125
3/4
✓ Branch 8 → 9 taken 56 times.
✓ Branch 8 → 11 taken 29 times.
✓ Branch 9 → 10 taken 56 times.
✗ Branch 9 → 11 not taken.
85 return allCaseBranchesReturn && defaultBranchReturns;
126 }
127
128 445 bool CaseBranchNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
129 445 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
130 }
131
132 69 bool DefaultBranchNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
133 69 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
134 }
135
136 45102 bool StmtLstNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
137 // An empty statement list does not return at all
138
2/2
✓ Branch 3 → 4 taken 167 times.
✓ Branch 3 → 5 taken 44935 times.
45102 if (statements.empty())
139 167 return false;
140 // A statement list returns on all control paths, if the one direct child statement returns on all control paths
141 44935 bool returnsOnAllControlPaths = false;
142
2/2
✓ Branch 26 → 7 taken 87634 times.
✓ Branch 26 → 27 taken 44935 times.
177504 for (StmtNode *child : statements) {
143
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 87634 times.
87634 assert(child != nullptr);
144
145 // Prevent marking instructions as unreachable if doSetPredecessorsUnreachable is set to false
146
4/4
✓ Branch 11 → 12 taken 1984 times.
✓ Branch 11 → 14 taken 85650 times.
✓ Branch 12 → 13 taken 20 times.
✓ Branch 12 → 14 taken 1964 times.
87634 if (returnsOnAllControlPaths && *doSetPredecessorsUnreachable)
147 20 child->unreachable = true;
148
149
3/4
✓ Branch 14 → 15 taken 87634 times.
✗ Branch 14 → 29 not taken.
✓ Branch 15 → 16 taken 28095 times.
✓ Branch 15 → 17 taken 59539 times.
87634 if (child->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx))
150 28095 returnsOnAllControlPaths = true;
151 }
152 44935 return returnsOnAllControlPaths;
153 }
154
155 5150 std::vector<const CompileTimeValue *> AttrLstNode::getAttrValuesByName(const std::string &key) const {
156
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 5150 times.
5150 assert(ATTR_CONFIGS.contains(key));
157
158 5150 std::vector<const CompileTimeValue *> attributeValues;
159
2/2
✓ Branch 28 → 7 taken 10201 times.
✓ Branch 28 → 29 taken 5150 times.
20501 for (const AttrNode *attrNode : attributes) {
160 // Skip attributes with different keys
161
2/2
✓ Branch 10 → 11 taken 7645 times.
✓ Branch 10 → 12 taken 2556 times.
10201 if (attrNode->key != key)
162 7645 continue;
163
164 // Found a matching attribute
165 2556 const CompileTimeValue *value = attrNode->getValue();
166
2/2
✓ Branch 13 → 14 taken 229 times.
✓ Branch 13 → 16 taken 2327 times.
2556 if (!value) {
167 // If the attribute has no value, we use the default value
168
1/2
✓ Branch 14 → 15 taken 229 times.
✗ Branch 14 → 31 not taken.
229 attributeValues.push_back(&DEFAULT_BOOL_COMPILE_VALUE);
169 } else {
170 // If the attribute has a value, we use the value
171
1/2
✓ Branch 16 → 17 taken 2327 times.
✗ Branch 16 → 32 not taken.
2327 attributeValues.push_back(value);
172 }
173 }
174
175 5150 return attributeValues;
176 }
177
178 3098 const CompileTimeValue *AttrLstNode::getAttrValueByName(const std::string &key) const {
179
1/2
✓ Branch 2 → 3 taken 3098 times.
✗ Branch 2 → 12 not taken.
3098 const std::vector<const CompileTimeValue *> attrs = getAttrValuesByName(key);
180
2/2
✓ Branch 4 → 5 taken 1949 times.
✓ Branch 4 → 6 taken 1149 times.
6196 return attrs.empty() ? nullptr : attrs.back();
181 3098 }
182
183 2900 bool AttrLstNode::hasAttr(const std::string &key) const {
184 8933 return std::ranges::any_of(attributes, [&](const AttrNode *attr) { return attr->key == key; });
185 }
186
187
2/2
✓ Branch 2 → 3 taken 2327 times.
✓ Branch 2 → 4 taken 229 times.
2556 const CompileTimeValue *AttrNode::getValue() const { return value ? &value->compileTimeValue : nullptr; }
188
189 3215 bool AssertStmtNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
190 // If the expression, passed to the assert statement is always evaluated to false, the assert statement will never succeed
191
4/4
✓ Branch 3 → 4 taken 17 times.
✓ Branch 3 → 7 taken 3198 times.
✓ Branch 5 → 6 taken 16 times.
✓ Branch 5 → 7 taken 1 time.
3215 return assignExpr->hasCompileTimeValue(manIdx) && !assignExpr->getCompileTimeValue(manIdx).boolValue;
192 }
193
194 15009 bool AssignExprNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
195 // If it's a ternary, do the default thing
196
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 15009 times.
15009 if (op == AssignOp::OP_NONE)
197 return ternaryExpr->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
198
199 // If it's a modification on the result variable, we technically return from the function, but at the end of the function.
200 15009 const AtomicExprNode *atomicExpr = getLhsAtomicNode();
201
6/6
✓ Branch 6 → 7 taken 14856 times.
✓ Branch 6 → 10 taken 153 times.
✓ Branch 8 → 9 taken 1604 times.
✓ Branch 8 → 10 taken 13252 times.
✓ Branch 11 → 12 taken 1604 times.
✓ Branch 11 → 13 taken 13405 times.
15009 if (atomicExpr && atomicExpr->fqIdentifier == RETURN_VARIABLE_NAME) {
202 // If we assign the result variable, we technically return from the function, but at the end of the function.
203 // Therefore, the following code is not unreachable, but will be executed in any case.
204 1604 *doSetPredecessorsUnreachable = false;
205 1604 return true;
206 }
207
208 13405 return false;
209 }
210
211 15009 AtomicExprNode *AssignExprNode::getLhsAtomicNode() const {
212
3/4
✓ Branch 2 → 3 taken 15009 times.
✗ Branch 2 → 4 not taken.
✓ Branch 5 → 6 taken 3058 times.
✓ Branch 5 → 7 taken 11951 times.
15009 if (auto *atomicNode = dynamic_cast<AtomicExprNode *>(lhs))
213 3058 return atomicNode;
214
4/6
✓ Branch 7 → 8 taken 11951 times.
✗ Branch 7 → 28 not taken.
✓ Branch 9 → 10 taken 11951 times.
✗ Branch 9 → 11 not taken.
✓ Branch 13 → 14 taken 11512 times.
✓ Branch 13 → 15 taken 439 times.
11951 if (auto *atomicNode = dynamic_cast<AtomicExprNode *>(lhs->getChildren().back()))
215 11512 return atomicNode;
216
5/8
✓ Branch 15 → 16 taken 439 times.
✗ Branch 15 → 31 not taken.
✓ Branch 17 → 18 taken 439 times.
✗ Branch 17 → 29 not taken.
✓ Branch 19 → 20 taken 439 times.
✗ Branch 19 → 21 not taken.
✓ Branch 24 → 25 taken 286 times.
✓ Branch 24 → 26 taken 153 times.
439 if (auto *atomicNode = dynamic_cast<AtomicExprNode *>(lhs->getChildren().back()->getChildren().front()))
217 286 return atomicNode;
218 153 return nullptr;
219 }
220
221 7 bool TernaryExprNode::hasCompileTimeValue(size_t manIdx) const {
222
3/4
✓ Branch 2 → 3 taken 7 times.
✗ Branch 2 → 5 not taken.
✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 6 taken 3 times.
7 const bool trueExprHasCompileTimeValue = !trueExpr || trueExpr->hasCompileTimeValue(manIdx);
223
3/4
✓ Branch 7 → 8 taken 7 times.
✗ Branch 7 → 10 not taken.
✓ Branch 9 → 10 taken 5 times.
✓ Branch 9 → 11 taken 2 times.
7 const bool falseExprHasCompileTimeValue = !falseExpr || falseExpr->hasCompileTimeValue(manIdx);
224
1/6
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 17 taken 7 times.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 17 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
7 return condition->hasCompileTimeValue(manIdx) && trueExprHasCompileTimeValue && falseExprHasCompileTimeValue;
225 }
226
227 CompileTimeValue TernaryExprNode::getCompileTimeValue(size_t manIdx) const {
228 assert(condition != nullptr);
229 if (!trueExpr && !falseExpr)
230 return condition->getCompileTimeValue(manIdx);
231
232 // If the condition has no compile time value, we do not need to evaluate the true and false values
233 if (!condition->hasCompileTimeValue(manIdx))
234 return {};
235
236 // Check if the condition always evaluates to 'true'
237 if (condition->getCompileTimeValue(manIdx).boolValue) {
238 const ExprNode *trueValue = isShortened ? condition : trueExpr;
239 assert(trueValue != nullptr);
240 return trueValue->getCompileTimeValue(manIdx);
241 }
242
243 assert(falseExpr != nullptr);
244 return falseExpr->getCompileTimeValue(manIdx);
245 }
246
247 674 bool LogicalOrExprNode::hasCompileTimeValue(size_t manIdx) const {
248 1348 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
249 }
250
251 CompileTimeValue LogicalOrExprNode::getCompileTimeValue(size_t manIdx) const {
252 if (operands.size() == 1)
253 return operands.front()->getCompileTimeValue(manIdx);
254
255 // Check if one expression evaluates to 'true'
256 for (const ExprNode *op : operands) {
257 assert(op->hasCompileTimeValue(manIdx));
258 // If one operand evaluates to 'true' the whole expression is 'true'
259 if (const CompileTimeValue opCompileTimeValue = op->getCompileTimeValue(manIdx); opCompileTimeValue.boolValue)
260 return CompileTimeValue{.boolValue = true};
261 }
262
263 // Return 'false'
264 return CompileTimeValue{.boolValue = false};
265 }
266
267 718 bool LogicalAndExprNode::hasCompileTimeValue(size_t manIdx) const {
268 1436 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
269 }
270
271 CompileTimeValue LogicalAndExprNode::getCompileTimeValue(size_t manIdx) const {
272 if (operands.size() == 1)
273 return operands.front()->getCompileTimeValue(manIdx);
274
275 // Check if all expressions evaluate to 'true'
276 for (const ExprNode *op : operands) {
277 assert(op->hasCompileTimeValue(manIdx));
278 // If one operand evaluates to 'false' the whole expression is 'false'
279 if (const CompileTimeValue opCompileTimeValue = op->getCompileTimeValue(manIdx); !opCompileTimeValue.boolValue)
280 return CompileTimeValue{.boolValue = false};
281 }
282
283 // Return 'false'
284 return CompileTimeValue{.boolValue = false};
285 }
286
287 204 bool BitwiseOrExprNode::hasCompileTimeValue(size_t manIdx) const {
288 408 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
289 }
290
291 CompileTimeValue BitwiseOrExprNode::getCompileTimeValue(size_t manIdx) const {
292 if (operands.size() == 1)
293 return operands.front()->getCompileTimeValue(manIdx);
294
295 CompileTimeValue result = operands.front()->getCompileTimeValue(manIdx);
296 for (size_t i = 1; i < operands.size(); i++) {
297 assert(operands.at(i)->hasCompileTimeValue(manIdx));
298 const CompileTimeValue opCompileTimeValue = operands.at(i)->getCompileTimeValue(manIdx);
299 result.longValue |= opCompileTimeValue.longValue;
300 }
301
302 return result;
303 }
304
305 1 bool BitwiseXorExprNode::hasCompileTimeValue(size_t manIdx) const {
306 2 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
307 }
308
309 CompileTimeValue BitwiseXorExprNode::getCompileTimeValue(size_t manIdx) const {
310 if (operands.size() == 1)
311 return operands.front()->getCompileTimeValue(manIdx);
312
313 CompileTimeValue result = operands.front()->getCompileTimeValue(manIdx);
314 for (size_t i = 1; i < operands.size(); i++) {
315 assert(operands.at(i)->hasCompileTimeValue(manIdx));
316 const CompileTimeValue opCompileTimeValue = operands.at(i)->getCompileTimeValue(manIdx);
317 result.longValue ^= opCompileTimeValue.longValue;
318 }
319
320 return result;
321 }
322
323 4 bool BitwiseAndExprNode::hasCompileTimeValue(size_t manIdx) const {
324 8 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
325 }
326
327 CompileTimeValue BitwiseAndExprNode::getCompileTimeValue(size_t manIdx) const {
328 if (operands.size() == 1)
329 return operands.front()->getCompileTimeValue(manIdx);
330
331 CompileTimeValue result = operands.front()->getCompileTimeValue(manIdx);
332 for (size_t i = 1; i < operands.size(); i++) {
333 assert(operands.at(i)->hasCompileTimeValue(manIdx));
334 const CompileTimeValue opCompileTimeValue = operands.at(i)->getCompileTimeValue(manIdx);
335 result.longValue &= opCompileTimeValue.longValue;
336 }
337
338 return result;
339 }
340
341 8159 bool EqualityExprNode::hasCompileTimeValue(size_t manIdx) const {
342 16320 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
343 }
344
345 3 CompileTimeValue EqualityExprNode::getCompileTimeValue(size_t manIdx) const {
346
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 3 times.
3 if (operands.size() == 1)
347 return operands.front()->getCompileTimeValue(manIdx);
348
349
2/4
✓ Branch 7 → 8 taken 3 times.
✗ Branch 7 → 34 not taken.
✓ Branch 8 → 9 taken 3 times.
✗ Branch 8 → 34 not taken.
3 const CompileTimeValue op0Value = operands.at(0)->getCompileTimeValue(manIdx);
350
2/4
✓ Branch 9 → 10 taken 3 times.
✗ Branch 9 → 34 not taken.
✓ Branch 10 → 11 taken 3 times.
✗ Branch 10 → 34 not taken.
3 const CompileTimeValue op1Value = operands.at(1)->getCompileTimeValue(manIdx);
351
2/2
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 13 taken 1 time.
3 if (op == EqualityOp::OP_EQUAL)
352 2 return CompileTimeValue{.boolValue = op0Value.longValue == op1Value.longValue};
353
1/2
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 15 not taken.
1 if (op == EqualityOp::OP_NOT_EQUAL)
354 1 return CompileTimeValue{.boolValue = op0Value.longValue != op1Value.longValue};
355
356 throw CompilerError(UNHANDLED_BRANCH, "EqualityExprNode::getCompileTimeValue()");
357 }
358
359 6487 bool RelationalExprNode::hasCompileTimeValue(size_t manIdx) const {
360 12995 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
361 }
362
363 41 CompileTimeValue RelationalExprNode::getCompileTimeValue(size_t manIdx) const {
364
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 41 times.
41 if (operands.size() == 1)
365 return operands.front()->getCompileTimeValue(manIdx);
366
367
2/4
✓ Branch 7 → 8 taken 41 times.
✗ Branch 7 → 38 not taken.
✓ Branch 8 → 9 taken 41 times.
✗ Branch 8 → 38 not taken.
41 const CompileTimeValue op0Value = operands.at(0)->getCompileTimeValue(manIdx);
368
2/4
✓ Branch 9 → 10 taken 41 times.
✗ Branch 9 → 38 not taken.
✓ Branch 10 → 11 taken 41 times.
✗ Branch 10 → 38 not taken.
41 const CompileTimeValue op1Value = operands.at(1)->getCompileTimeValue(manIdx);
369
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 41 times.
41 if (op == RelationalOp::OP_LESS)
370 return CompileTimeValue{.boolValue = op0Value.longValue < op1Value.longValue};
371
2/2
✓ Branch 13 → 14 taken 40 times.
✓ Branch 13 → 15 taken 1 time.
41 if (op == RelationalOp::OP_GREATER)
372 40 return CompileTimeValue{.boolValue = op0Value.longValue > op1Value.longValue};
373
1/2
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 17 not taken.
1 if (op == RelationalOp::OP_LESS_EQUAL)
374 1 return CompileTimeValue{.boolValue = op0Value.longValue <= op1Value.longValue};
375 if (op == RelationalOp::OP_GREATER_EQUAL)
376 return CompileTimeValue{.boolValue = op0Value.longValue >= op1Value.longValue};
377
378 throw CompilerError(UNHANDLED_BRANCH, "RelationalExprNode::getCompileTimeValue()");
379 }
380
381 bool ShiftExprNode::hasCompileTimeValue(size_t manIdx) const {
382 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
383 }
384
385 CompileTimeValue ShiftExprNode::getCompileTimeValue(size_t manIdx) const {
386 if (operands.size() == 1)
387 return operands.front()->getCompileTimeValue(manIdx);
388
389 CompileTimeValue result = operands.front()->getCompileTimeValue(manIdx);
390 OpQueue opQueueCopy = opQueue;
391 for (size_t i = 1; i < operands.size(); i++) {
392 assert(operands.at(i)->hasCompileTimeValue(manIdx));
393 const CompileTimeValue opCompileTimeValue = operands.at(i)->getCompileTimeValue(manIdx);
394 const ShiftOp op = opQueueCopy.front().first;
395 opQueueCopy.pop();
396 if (op == ShiftOp::OP_SHIFT_LEFT)
397 result.longValue <<= opCompileTimeValue.longValue;
398 else if (op == ShiftOp::OP_SHIFT_RIGHT)
399 result.longValue >>= opCompileTimeValue.longValue;
400 else
401 throw CompilerError(UNHANDLED_BRANCH, "ShiftExprNode::getCompileTimeValue()");
402 }
403 return result;
404 }
405
406 558 bool AdditiveExprNode::hasCompileTimeValue(size_t manIdx) const {
407 1117 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
408 }
409
410 CompileTimeValue AdditiveExprNode::getCompileTimeValue(size_t manIdx) const {
411 if (operands.size() == 1)
412 return operands.front()->getCompileTimeValue(manIdx);
413
414 CompileTimeValue result = operands.front()->getCompileTimeValue(manIdx);
415 OpQueue opQueueCopy = opQueue;
416 for (size_t i = 1; i < operands.size(); i++) {
417 assert(operands.at(i)->hasCompileTimeValue(manIdx));
418 const CompileTimeValue opCompileTimeValue = operands.at(i)->getCompileTimeValue(manIdx);
419 const AdditiveOp op = opQueueCopy.front().first;
420 opQueueCopy.pop();
421 if (op == AdditiveOp::OP_PLUS)
422 result.longValue += opCompileTimeValue.longValue;
423 else if (op == AdditiveOp::OP_MINUS)
424 result.longValue -= opCompileTimeValue.longValue;
425 else
426 throw CompilerError(UNHANDLED_BRANCH, "AdditiveExprNode::getCompileTimeValue()");
427 }
428 return result;
429 }
430
431 363 bool MultiplicativeExprNode::hasCompileTimeValue(size_t manIdx) const {
432 727 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
433 }
434
435 CompileTimeValue MultiplicativeExprNode::getCompileTimeValue(size_t manIdx) const {
436 if (operands.size() == 1)
437 return operands.front()->getCompileTimeValue(manIdx);
438
439 CompileTimeValue result = operands.front()->getCompileTimeValue(manIdx);
440 OpQueue opQueueCopy = opQueue;
441 for (size_t i = 1; i < operands.size(); i++) {
442 assert(operands.at(i)->hasCompileTimeValue(manIdx));
443 const CompileTimeValue opCompileTimeValue = operands.at(i)->getCompileTimeValue(manIdx);
444 const MultiplicativeOp op = opQueueCopy.front().first;
445 opQueueCopy.pop();
446 if (op == MultiplicativeOp::OP_MUL) {
447 result.longValue *= opCompileTimeValue.longValue;
448 } else if (op == MultiplicativeOp::OP_DIV) {
449 if (opCompileTimeValue.longValue == 0)
450 throw SemanticError(operands.at(i), DIVISION_BY_ZERO, "Dividing by zero is not allowed.");
451 result.longValue /= opCompileTimeValue.longValue;
452 } else if (op == MultiplicativeOp::OP_REM) {
453 result.longValue %= opCompileTimeValue.longValue;
454 } else {
455 throw CompilerError(UNHANDLED_BRANCH, "MultiplicativeExprNode::getCompileTimeValue()");
456 }
457 }
458 return result;
459 }
460
461 540 bool CastExprNode::hasCompileTimeValue(size_t manIdx) const {
462
1/2
✓ Branch 2 → 3 taken 540 times.
✗ Branch 2 → 5 not taken.
540 return isCast ? assignExpr->hasCompileTimeValue(manIdx) : prefixUnaryExpr->hasCompileTimeValue(manIdx);
463 }
464
465 CompileTimeValue CastExprNode::getCompileTimeValue(size_t manIdx) const {
466 return isCast ? assignExpr->getCompileTimeValue(manIdx) : prefixUnaryExpr->getCompileTimeValue(manIdx);
467 }
468
469 2124 bool PrefixUnaryExprNode::hasCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
470
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 2124 times.
2124 if (postfixUnaryExpr)
471 return postfixUnaryExpr->hasCompileTimeValue(manIdx);
472
473
3/4
✓ Branch 6 → 7 taken 2091 times.
✓ Branch 6 → 11 taken 33 times.
✓ Branch 7 → 8 taken 2091 times.
✗ Branch 7 → 11 not taken.
2124 const bool isSupported = op == PrefixUnaryOp::OP_NONE || op == PrefixUnaryOp::OP_MINUS || op == PrefixUnaryOp::OP_PLUS_PLUS ||
474
4/6
✓ Branch 5 → 6 taken 2124 times.
✗ Branch 5 → 11 not taken.
✓ Branch 8 → 9 taken 2091 times.
✗ Branch 8 → 11 not taken.
✓ Branch 9 → 10 taken 46 times.
✓ Branch 9 → 11 taken 2045 times.
4294 op == PrefixUnaryOp::OP_MINUS_MINUS || op == PrefixUnaryOp::OP_NOT ||
475
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 45 times.
46 op == PrefixUnaryOp::OP_BITWISE_NOT;
476
4/4
✓ Branch 13 → 14 taken 2079 times.
✓ Branch 13 → 17 taken 45 times.
✓ Branch 15 → 16 taken 65 times.
✓ Branch 15 → 17 taken 2014 times.
2124 return isSupported && prefixUnaryExpr->hasCompileTimeValue(manIdx);
477 }
478
479 130 CompileTimeValue PrefixUnaryExprNode::getCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
480
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 130 times.
130 if (postfixUnaryExpr)
481 return postfixUnaryExpr->getCompileTimeValue(manIdx);
482
483
1/2
✓ Branch 5 → 6 taken 130 times.
✗ Branch 5 → 35 not taken.
130 CompileTimeValue opValue = prefixUnaryExpr->getCompileTimeValue(manIdx);
484
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 130 times.
130 if (op == PrefixUnaryOp::OP_MINUS)
485 return CompileTimeValue{.longValue = -opValue.longValue};
486
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 130 times.
130 if (op == PrefixUnaryOp::OP_PLUS_PLUS)
487 return CompileTimeValue{.longValue = ++opValue.longValue};
488
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 130 times.
130 if (op == PrefixUnaryOp::OP_MINUS_MINUS)
489 return CompileTimeValue{.longValue = --opValue.longValue};
490
1/2
✓ Branch 12 → 13 taken 130 times.
✗ Branch 12 → 14 not taken.
130 if (op == PrefixUnaryOp::OP_NOT)
491 130 return CompileTimeValue{.boolValue = !opValue.boolValue};
492 if (op == PrefixUnaryOp::OP_BITWISE_NOT)
493 return CompileTimeValue{.longValue = ~opValue.longValue};
494
495 throw CompilerError(UNHANDLED_BRANCH, "PrefixUnaryExprNode::getCompileTimeValue()");
496 }
497
498 5153 bool PostfixUnaryExprNode::hasCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
499
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 5153 times.
5153 if (atomicExpr)
500 return atomicExpr->hasCompileTimeValue(manIdx);
501
502 5153 const bool isSupported =
503
4/6
✓ Branch 5 → 6 taken 5153 times.
✗ Branch 5 → 8 not taken.
✓ Branch 6 → 7 taken 5153 times.
✗ Branch 6 → 8 not taken.
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 9 taken 5151 times.
5153 op == PostfixUnaryOp::OP_NONE || op == PostfixUnaryOp::OP_PLUS_PLUS || op == PostfixUnaryOp::OP_MINUS_MINUS;
504
3/4
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 14 taken 5151 times.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 2 times.
5153 return isSupported && postfixUnaryExpr->hasCompileTimeValue(manIdx);
505 }
506
507 CompileTimeValue PostfixUnaryExprNode::getCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
508 if (atomicExpr)
509 return atomicExpr->getCompileTimeValue(manIdx);
510
511 CompileTimeValue opValue = postfixUnaryExpr->getCompileTimeValue(manIdx);
512 if (op == PostfixUnaryOp::OP_PLUS_PLUS)
513 return CompileTimeValue{.longValue = opValue.longValue++};
514 if (op == PostfixUnaryOp::OP_MINUS_MINUS)
515 return CompileTimeValue{.longValue = opValue.longValue--};
516
517 throw CompilerError(UNHANDLED_BRANCH, "PostfixUnaryExprNode::getCompileTimeValue()");
518 }
519
520
4/4
✓ Branch 2 → 3 taken 6190 times.
✓ Branch 2 → 5 taken 16 times.
✓ Branch 4 → 5 taken 85 times.
✓ Branch 4 → 6 taken 6105 times.
6206 bool ValueNode::hasCompileTimeValue(size_t manIdx) const { return isNil || ASTNode::hasCompileTimeValue(manIdx); }
521
522 170 CompileTimeValue ValueNode::getCompileTimeValue(size_t manIdx) const {
523
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 170 times.
170 return isNil ? CompileTimeValue{.longValue = 0} : ASTNode::getCompileTimeValue(manIdx);
524 }
525
526 11536 bool FctCallNode::hasCompileTimeValue(size_t manIdx) const {
527
6/8
✓ Branch 3 → 4 taken 11536 times.
✗ Branch 3 → 11 not taken.
✓ Branch 4 → 5 taken 5478 times.
✓ Branch 4 → 8 taken 6058 times.
✓ Branch 5 → 6 taken 5478 times.
✗ Branch 5 → 11 not taken.
✓ Branch 6 → 7 taken 1533 times.
✓ Branch 6 → 8 taken 3945 times.
11536 return BUILTIN_FUNCTIONS_MAP.contains(fqFunctionName) && data.at(manIdx).compileTimeValueSet;
528 }
529
530 1618 CompileTimeValue FctCallNode::getCompileTimeValue(size_t manIdx) const { return data.at(manIdx).compileTimeValue; }
531
532 127 void FctCallNode::setCompileTimeValue(const CompileTimeValue &value, size_t manIdx) {
533 127 data.at(manIdx).setCompileTimeValue(value);
534 127 }
535
536 13706 bool FctCallNode::returnsOnAllControlPaths(bool *overrideUnreachable, size_t manIdx) const {
537 // Some builtin functions terminate the control flow, e.g. panic
538
1/2
✓ Branch 3 → 4 taken 13706 times.
✗ Branch 3 → 13 not taken.
13706 const auto it = BUILTIN_FUNCTIONS_MAP.find(fqFunctionName);
539
4/4
✓ Branch 6 → 7 taken 3618 times.
✓ Branch 6 → 10 taken 10088 times.
✓ Branch 8 → 9 taken 2322 times.
✓ Branch 8 → 10 taken 1296 times.
13706 return it != BUILTIN_FUNCTIONS_MAP.end() && it->second.isFunctionTerminator;
540 }
541
542 /**
543 * Check if right above the closest assign expression ancestor is a statement node
544 *
545 * @return Has return value receiver or not
546 */
547 32231 bool FctCallNode::hasReturnValueReceiver() const {
548 32231 const ASTNode *node = parent;
549
2/2
✓ Branch 13 → 3 taken 113058 times.
✓ Branch 13 → 14 taken 2749 times.
115807 while (!node->isAssignExpr()) {
550
2/2
✓ Branch 4 → 5 taken 1562 times.
✓ Branch 4 → 6 taken 111496 times.
113058 if (node->isExprStmt())
551 1562 return false;
552 // As soon as we have a node with more than one child, we know that the return value is used
553
3/4
✓ Branch 6 → 7 taken 111496 times.
✗ Branch 6 → 25 not taken.
✓ Branch 9 → 10 taken 27920 times.
✓ Branch 9 → 11 taken 83576 times.
111496 if (node->getChildren().size() > 1)
554 27920 return true;
555 83576 node = node->parent;
556 }
557 // Also check the condition of the assign expression
558
3/12
✓ Branch 14 → 15 taken 2749 times.
✗ Branch 14 → 26 not taken.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 19 taken 2749 times.
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 26 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 20 not taken.
✓ Branch 21 → 22 taken 2749 times.
✗ Branch 21 → 23 not taken.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
2749 return node->getChildren().size() > 1 || !node->parent->isExprStmt();
559 }
560
561 22 bool LambdaFuncNode::returnsOnAllControlPaths(bool *overrideUnreachable, size_t manIdx) const {
562 22 return body->returnsOnAllControlPaths(overrideUnreachable, manIdx);
563 }
564
565 42 bool LambdaProcNode::returnsOnAllControlPaths(bool *overrideUnreachable, size_t manIdx) const {
566 42 return body->returnsOnAllControlPaths(overrideUnreachable, manIdx);
567 }
568
569 6164 void DataTypeNode::setFieldTypeRecursive() { // NOLINT(*-no-recursion)
570 // Set the current node to field type
571 6164 isFieldType = true;
572 // Do the same for all template nodes
573
4/4
✓ Branch 2 → 3 taken 3844 times.
✓ Branch 2 → 20 taken 2320 times.
✓ Branch 3 → 4 taken 658 times.
✓ Branch 3 → 20 taken 3186 times.
6164 if (const CustomDataTypeNode *customType = baseDataType->customDataType; customType != nullptr && customType->templateTypeLst)
574
2/2
✓ Branch 18 → 6 taken 858 times.
✓ Branch 18 → 19 taken 658 times.
2174 for (DataTypeNode *templateNode : customType->templateTypeLst->dataTypes)
575
1/2
✓ Branch 8 → 9 taken 858 times.
✗ Branch 8 → 21 not taken.
858 templateNode->setFieldTypeRecursive();
576 6164 }
577
578 } // namespace spice::compiler
579