GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 55.9% 194 / 0 / 347
Functions: 77.5% 55 / 0 / 71
Branches: 36.6% 196 / 0 / 536

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 972 std::string ASTNode::getErrorMessage() const {
17 972 antlr4::CharStream *inputStream = codeLoc.sourceFile->antlrCtx.inputStream.get();
18 972 const antlr4::misc::Interval &sourceInterval = codeLoc.sourceInterval;
19 972 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 972 times.
✗ Branch 3 → 77 not taken.
✓ Branch 6 → 7 taken 19 times.
✓ Branch 6 → 8 taken 953 times.
972 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 972 size_t markerIndentation = 0;
26
2/2
✓ Branch 20 → 9 taken 10388 times.
✓ Branch 20 → 21 taken 238 times.
10626 for (; markerIndentation < ERROR_MESSAGE_CONTEXT; markerIndentation++) {
27 10388 extSourceInterval.a--;
28
9/12
✓ Branch 9 → 10 taken 10374 times.
✓ Branch 9 → 13 taken 14 times.
✓ Branch 10 → 11 taken 10374 times.
✗ Branch 10 → 78 not taken.
✓ Branch 12 → 13 taken 720 times.
✓ Branch 12 → 14 taken 9654 times.
✓ Branch 15 → 16 taken 10374 times.
✓ Branch 15 → 17 taken 14 times.
✓ Branch 17 → 18 taken 734 times.
✓ Branch 17 → 19 taken 9654 times.
✗ Branch 78 → 79 not taken.
✗ Branch 78 → 80 not taken.
10388 if (extSourceInterval.a < 0 || inputStream->getText(extSourceInterval).find('\n') != std::string::npos) {
29 734 extSourceInterval.a++;
30 734 break;
31 }
32 }
33
2/2
✓ Branch 34 → 22 taken 3519 times.
✓ Branch 34 → 35 taken 43 times.
3562 for (size_t suffixContext = 0; suffixContext < ERROR_MESSAGE_CONTEXT; suffixContext++) {
34 3519 extSourceInterval.b++;
35
4/6
✓ Branch 22 → 23 taken 3519 times.
✗ Branch 22 → 82 not taken.
✓ Branch 23 → 24 taken 3519 times.
✗ Branch 23 → 27 not taken.
✓ Branch 26 → 27 taken 929 times.
✓ Branch 26 → 28 taken 2590 times.
7038 if (static_cast<size_t>(extSourceInterval.b) > inputStream->size() ||
36
4/8
✓ Branch 24 → 25 taken 3519 times.
✗ Branch 24 → 82 not taken.
✓ Branch 29 → 30 taken 3519 times.
✗ Branch 29 → 31 not taken.
✓ Branch 31 → 32 taken 929 times.
✓ Branch 31 → 33 taken 2590 times.
✗ Branch 82 → 83 not taken.
✗ Branch 82 → 84 not taken.
7038 inputStream->getText(extSourceInterval).find('\n') != std::string::npos) {
37 929 extSourceInterval.b--;
38 929 break;
39 }
40 }
41
42 // Trim start
43
3/4
✓ Branch 37 → 38 taken 5560 times.
✗ Branch 37 → 86 not taken.
✓ Branch 40 → 36 taken 4588 times.
✓ Branch 40 → 41 taken 972 times.
5560 while (inputStream->getText(extSourceInterval)[0] == ' ') {
44 4588 extSourceInterval.a++;
45 4588 markerIndentation--;
46 }
47
48 // Trim end
49
3/4
✓ Branch 41 → 42 taken 972 times.
✗ Branch 41 → 87 not taken.
✓ Branch 45 → 46 taken 19 times.
✓ Branch 45 → 47 taken 953 times.
972 if (inputStream->getText(extSourceInterval)[extSourceInterval.length() - 1] == '\n')
50 19 extSourceInterval.b--;
51
52 972 const std::string lineNumberStr = std::to_string(codeLoc.line);
53 972 markerIndentation += lineNumberStr.length() + 2;
54
55 // Build error message
56
1/2
✓ Branch 49 → 50 taken 972 times.
✗ Branch 49 → 107 not taken.
972 std::stringstream ss;
57
5/10
✓ Branch 50 → 51 taken 972 times.
✗ Branch 50 → 105 not taken.
✓ Branch 51 → 52 taken 972 times.
✗ Branch 51 → 105 not taken.
✓ Branch 52 → 53 taken 972 times.
✗ Branch 52 → 90 not taken.
✓ Branch 53 → 54 taken 972 times.
✗ Branch 53 → 88 not taken.
✓ Branch 54 → 55 taken 972 times.
✗ Branch 54 → 88 not taken.
972 ss << lineNumberStr << " " << inputStream->getText(extSourceInterval) << "\n";
58
2/4
✓ Branch 58 → 59 taken 972 times.
✗ Branch 58 → 93 not taken.
✓ Branch 59 → 60 taken 972 times.
✗ Branch 59 → 91 not taken.
1944 ss << std::string(markerIndentation, ' ');
59
2/4
✓ Branch 67 → 68 taken 972 times.
✗ Branch 67 → 99 not taken.
✓ Branch 68 → 69 taken 972 times.
✗ Branch 68 → 97 not taken.
972 ss << std::string(std::min(sourceInterval.length(), extSourceInterval.length()), '^');
60
1/2
✓ Branch 71 → 72 taken 972 times.
✗ Branch 71 → 105 not taken.
1944 return ss.str();
61 972 }
62
63 24681 const StmtLstNode *ASTNode::getNextOuterStmtLst() const { // NOLINT(*-no-recursion)
64
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 24681 times.
24681 assert(parent != nullptr);
65
2/2
✓ Branch 5 → 6 taken 21765 times.
✓ Branch 5 → 14 taken 2916 times.
46446 return isStmtLst() ? spice_pointer_cast<const StmtLstNode *>(this) : parent->getNextOuterStmtLst();
66 }
67
68 404 bool MainFctDefNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
69 404 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
70 }
71
72 12738 bool FctDefBaseNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
73 12738 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
74 }
75
76 46 CompileTimeValue GlobalVarDefNode::getCompileTimeValue(size_t manIdx) const { return constant->getCompileTimeValue(manIdx); }
77
78 1244 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 1243 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 1 time.
1244 const bool condAlwaysTrue = condAssign->hasCompileTimeValue(manIdx) && condAssign->getCompileTimeValue(manIdx).boolValue;
82
1/4
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 12 taken 1244 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
1244 return condAlwaysTrue && body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
83 }
84
85 812 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 7 times.
✓ Branch 3 → 7 taken 805 times.
✓ Branch 5 → 6 taken 7 times.
✗ Branch 5 → 7 not taken.
812 const bool condAlwaysTrue = condition->hasCompileTimeValue(manIdx) && condition->getCompileTimeValue(manIdx).boolValue;
89
3/4
✓ Branch 8 → 9 taken 7 times.
✓ Branch 8 → 12 taken 805 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 7 times.
812 return condAlwaysTrue && body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
90 }
91
92 8 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 8 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
95 }
96
97 3662 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 2 → 3 not taken.
✓ Branch 2 → 5 taken 3662 times.
3662 if (!compileElseBranch)
100 return thenBody->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
101
102 // If the condition always evaluates to 'false' the else block must return
103
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 12 taken 3662 times.
3662 if (!compileThenBranch)
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 13 → 14 taken 2742 times.
✓ Branch 13 → 18 taken 920 times.
✓ Branch 14 → 15 taken 31 times.
✓ Branch 14 → 18 taken 2711 times.
3693 return thenBody->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx) && elseStmt != nullptr &&
108
2/2
✓ Branch 16 → 17 taken 30 times.
✓ Branch 16 → 18 taken 1 time.
3693 elseStmt->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
109 }
110
111 31 bool ElseStmtNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable,
112 size_t manIdx) const { // NOLINT(misc-no-recursion)
113
2/2
✓ Branch 2 → 3 taken 9 times.
✓ Branch 2 → 5 taken 22 times.
31 if (isElseIf)
114 9 return ifStmt->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
115 22 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
116 }
117
118 12 bool SwitchStmtNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
119 42 const auto pred = [=](const CaseBranchNode *node) {
120 42 return node->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
121 12 };
122
1/2
✓ Branch 2 → 3 taken 12 times.
✗ Branch 2 → 15 not taken.
12 const bool allCaseBranchesReturn = std::ranges::all_of(caseBranches, pred);
123 const bool defaultBranchReturns =
124
4/6
✓ Branch 3 → 4 taken 6 times.
✓ Branch 3 → 6 taken 6 times.
✓ Branch 4 → 5 taken 6 times.
✗ Branch 4 → 15 not taken.
✓ Branch 5 → 6 taken 6 times.
✗ Branch 5 → 7 not taken.
12 !defaultBranch || defaultBranch->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
125
3/4
✓ Branch 8 → 9 taken 9 times.
✓ Branch 8 → 11 taken 3 times.
✓ Branch 9 → 10 taken 9 times.
✗ Branch 9 → 11 not taken.
24 return allCaseBranchesReturn && defaultBranchReturns;
126 }
127
128 42 bool CaseBranchNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
129 42 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
130 }
131
132 6 bool DefaultBranchNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
133 6 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
134 }
135
136 19358 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 48 times.
✓ Branch 3 → 5 taken 19310 times.
19358 if (statements.empty())
139 48 return false;
140 // A statement list returns on all control paths, if the one direct child statement returns on all control paths
141 19310 bool returnsOnAllControlPaths = false;
142
2/2
✓ Branch 18 → 7 taken 37288 times.
✓ Branch 18 → 19 taken 19310 times.
56598 for (StmtNode *child : statements) {
143
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 37288 times.
37288 assert(child != nullptr);
144
145 // Prevent marking instructions as unreachable if doSetPredecessorsUnreachable is set to false
146
4/4
✓ Branch 10 → 11 taken 1014 times.
✓ Branch 10 → 13 taken 36274 times.
✓ Branch 11 → 12 taken 3 times.
✓ Branch 11 → 13 taken 1011 times.
37288 if (returnsOnAllControlPaths && *doSetPredecessorsUnreachable)
147 3 child->unreachable = true;
148
149
3/4
✓ Branch 13 → 14 taken 37288 times.
✗ Branch 13 → 21 not taken.
✓ Branch 14 → 15 taken 11982 times.
✓ Branch 14 → 16 taken 25306 times.
37288 if (child->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx))
150 11982 returnsOnAllControlPaths = true;
151 }
152 19310 return returnsOnAllControlPaths;
153 }
154
155 2734 std::vector<const CompileTimeValue *> AttrLstNode::getAttrValuesByName(const std::string &key) const {
156
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2734 times.
2734 assert(ATTR_CONFIGS.contains(key));
157
158 2734 std::vector<const CompileTimeValue *> attributeValues;
159
2/2
✓ Branch 19 → 7 taken 3630 times.
✓ Branch 19 → 20 taken 2734 times.
6364 for (const AttrNode *attrNode : attributes) {
160 // Skip attributes with different keys
161
2/2
✓ Branch 9 → 10 taken 2805 times.
✓ Branch 9 → 11 taken 825 times.
3630 if (attrNode->key != key)
162 2805 continue;
163
164 // Found a matching attribute
165 825 const CompileTimeValue *value = attrNode->getValue();
166
2/2
✓ Branch 12 → 13 taken 44 times.
✓ Branch 12 → 15 taken 781 times.
825 if (!value) {
167 // If the attribute has no value, we use the default value
168
1/2
✓ Branch 13 → 14 taken 44 times.
✗ Branch 13 → 22 not taken.
44 attributeValues.push_back(&DEFAULT_BOOL_COMPILE_VALUE);
169 } else {
170 // If the attribute has a value, we use the value
171
1/2
✓ Branch 15 → 16 taken 781 times.
✗ Branch 15 → 23 not taken.
781 attributeValues.push_back(value);
172 }
173 }
174
175 2734 return attributeValues;
176 }
177
178 1597 const CompileTimeValue *AttrLstNode::getAttrValueByName(const std::string &key) const {
179
1/2
✓ Branch 2 → 3 taken 1597 times.
✗ Branch 2 → 12 not taken.
1597 const std::vector<const CompileTimeValue *> attrs = getAttrValuesByName(key);
180
2/2
✓ Branch 4 → 5 taken 1034 times.
✓ Branch 4 → 6 taken 563 times.
3194 return attrs.empty() ? nullptr : attrs.back();
181 1597 }
182
183 1449 bool AttrLstNode::hasAttr(const std::string &key) const {
184 3475 return std::ranges::any_of(attributes, [&](const AttrNode *attr) { return attr->key == key; });
185 }
186
187
2/2
✓ Branch 2 → 3 taken 781 times.
✓ Branch 2 → 4 taken 44 times.
825 const CompileTimeValue *AttrNode::getValue() const { return value ? &value->compileTimeValue : nullptr; }
188
189 778 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 4 times.
✓ Branch 3 → 7 taken 774 times.
✓ Branch 5 → 6 taken 3 times.
✓ Branch 5 → 7 taken 1 time.
778 return assignExpr->hasCompileTimeValue(manIdx) && !assignExpr->getCompileTimeValue(manIdx).boolValue;
192 }
193
194 6449 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 6449 times.
6449 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 6449 const AtomicExprNode *atomicExpr = getLhsAtomicNode();
201
6/6
✓ Branch 6 → 7 taken 6326 times.
✓ Branch 6 → 10 taken 123 times.
✓ Branch 8 → 9 taken 813 times.
✓ Branch 8 → 10 taken 5513 times.
✓ Branch 11 → 12 taken 813 times.
✓ Branch 11 → 13 taken 5636 times.
6449 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 813 *doSetPredecessorsUnreachable = false;
205 813 return true;
206 }
207
208 5636 return false;
209 }
210
211 6449 AtomicExprNode *AssignExprNode::getLhsAtomicNode() const {
212
3/4
✓ Branch 2 → 3 taken 6449 times.
✗ Branch 2 → 4 not taken.
✓ Branch 5 → 6 taken 1452 times.
✓ Branch 5 → 7 taken 4997 times.
6449 if (auto *atomicNode = dynamic_cast<AtomicExprNode *>(lhs))
213 1452 return atomicNode;
214
4/6
✓ Branch 7 → 8 taken 4997 times.
✗ Branch 7 → 28 not taken.
✓ Branch 9 → 10 taken 4997 times.
✗ Branch 9 → 11 not taken.
✓ Branch 13 → 14 taken 4753 times.
✓ Branch 13 → 15 taken 244 times.
4997 if (auto *atomicNode = dynamic_cast<AtomicExprNode *>(lhs->getChildren().back()))
215 4753 return atomicNode;
216
5/8
✓ Branch 15 → 16 taken 244 times.
✗ Branch 15 → 31 not taken.
✓ Branch 17 → 18 taken 244 times.
✗ Branch 17 → 29 not taken.
✓ Branch 19 → 20 taken 244 times.
✗ Branch 19 → 21 not taken.
✓ Branch 24 → 25 taken 121 times.
✓ Branch 24 → 26 taken 123 times.
244 if (auto *atomicNode = dynamic_cast<AtomicExprNode *>(lhs->getChildren().back()->getChildren().front()))
217 121 return atomicNode;
218 123 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 355 bool LogicalOrExprNode::hasCompileTimeValue(size_t manIdx) const {
248 710 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 80 bool LogicalAndExprNode::hasCompileTimeValue(size_t manIdx) const {
268 160 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 80 bool BitwiseOrExprNode::hasCompileTimeValue(size_t manIdx) const {
288 160 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 bool BitwiseXorExprNode::hasCompileTimeValue(size_t manIdx) const {
306 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 2 bool BitwiseAndExprNode::hasCompileTimeValue(size_t manIdx) const {
324 4 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 3549 bool EqualityExprNode::hasCompileTimeValue(size_t manIdx) const {
342 7100 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 3079 bool RelationalExprNode::hasCompileTimeValue(size_t manIdx) const {
360 6165 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
361 }
362
363 13 CompileTimeValue RelationalExprNode::getCompileTimeValue(size_t manIdx) const {
364
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 13 times.
13 if (operands.size() == 1)
365 return operands.front()->getCompileTimeValue(manIdx);
366
367
2/4
✓ Branch 7 → 8 taken 13 times.
✗ Branch 7 → 38 not taken.
✓ Branch 8 → 9 taken 13 times.
✗ Branch 8 → 38 not taken.
13 const CompileTimeValue op0Value = operands.at(0)->getCompileTimeValue(manIdx);
368
2/4
✓ Branch 9 → 10 taken 13 times.
✗ Branch 9 → 38 not taken.
✓ Branch 10 → 11 taken 13 times.
✗ Branch 10 → 38 not taken.
13 const CompileTimeValue op1Value = operands.at(1)->getCompileTimeValue(manIdx);
369
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 13 times.
13 if (op == RelationalOp::OP_LESS)
370 return CompileTimeValue{.boolValue = op0Value.longValue < op1Value.longValue};
371
2/2
✓ Branch 13 → 14 taken 12 times.
✓ Branch 13 → 15 taken 1 time.
13 if (op == RelationalOp::OP_GREATER)
372 12 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 170 bool AdditiveExprNode::hasCompileTimeValue(size_t manIdx) const {
407 340 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 201 bool MultiplicativeExprNode::hasCompileTimeValue(size_t manIdx) const {
432 403 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 326 bool CastExprNode::hasCompileTimeValue(size_t manIdx) const {
462
1/2
✓ Branch 2 → 3 taken 326 times.
✗ Branch 2 → 5 not taken.
326 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 426 bool PrefixUnaryExprNode::hasCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
470
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 426 times.
426 if (postfixUnaryExpr)
471 return postfixUnaryExpr->hasCompileTimeValue(manIdx);
472
473
3/4
✓ Branch 6 → 7 taken 418 times.
✓ Branch 6 → 11 taken 8 times.
✓ Branch 7 → 8 taken 418 times.
✗ Branch 7 → 11 not taken.
426 const bool isSupported = op == PrefixUnaryOp::OP_NONE || op == PrefixUnaryOp::OP_MINUS || op == PrefixUnaryOp::OP_PLUS_PLUS ||
474
4/6
✓ Branch 5 → 6 taken 426 times.
✗ Branch 5 → 11 not taken.
✓ Branch 8 → 9 taken 418 times.
✗ Branch 8 → 11 not taken.
✓ Branch 9 → 10 taken 22 times.
✓ Branch 9 → 11 taken 396 times.
874 op == PrefixUnaryOp::OP_MINUS_MINUS || op == PrefixUnaryOp::OP_NOT ||
475
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 22 times.
22 op == PrefixUnaryOp::OP_BITWISE_NOT;
476
3/4
✓ Branch 13 → 14 taken 404 times.
✓ Branch 13 → 17 taken 22 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 404 times.
426 return isSupported && prefixUnaryExpr->hasCompileTimeValue(manIdx);
477 }
478
479 CompileTimeValue PrefixUnaryExprNode::getCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
480 if (postfixUnaryExpr)
481 return postfixUnaryExpr->getCompileTimeValue(manIdx);
482
483 CompileTimeValue opValue = prefixUnaryExpr->getCompileTimeValue(manIdx);
484 if (op == PrefixUnaryOp::OP_MINUS)
485 return CompileTimeValue{.longValue = -opValue.longValue};
486 if (op == PrefixUnaryOp::OP_PLUS_PLUS)
487 return CompileTimeValue{.longValue = ++opValue.longValue};
488 if (op == PrefixUnaryOp::OP_MINUS_MINUS)
489 return CompileTimeValue{.longValue = --opValue.longValue};
490 if (op == PrefixUnaryOp::OP_NOT)
491 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 2298 bool PostfixUnaryExprNode::hasCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
499
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 2298 times.
2298 if (atomicExpr)
500 return atomicExpr->hasCompileTimeValue(manIdx);
501
502 2298 const bool isSupported =
503
3/6
✓ Branch 5 → 6 taken 2298 times.
✗ Branch 5 → 8 not taken.
✓ Branch 6 → 7 taken 2298 times.
✗ Branch 6 → 8 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 2298 times.
2298 op == PostfixUnaryOp::OP_NONE || op == PostfixUnaryOp::OP_PLUS_PLUS || op == PostfixUnaryOp::OP_MINUS_MINUS;
504
1/4
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 14 taken 2298 times.
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 14 not taken.
2298 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 1423 times.
✓ Branch 2 → 5 taken 18 times.
✓ Branch 4 → 5 taken 6 times.
✓ Branch 4 → 6 taken 1417 times.
1441 bool ValueNode::hasCompileTimeValue(size_t manIdx) const { return isNil || ASTNode::hasCompileTimeValue(manIdx); }
521
522 12 CompileTimeValue ValueNode::getCompileTimeValue(size_t manIdx) const {
523
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 12 times.
12 return isNil ? CompileTimeValue{.longValue = 0} : ASTNode::getCompileTimeValue(manIdx);
524 }
525
526 3198 bool FctCallNode::hasCompileTimeValue(size_t manIdx) const {
527
6/8
✓ Branch 3 → 4 taken 3198 times.
✗ Branch 3 → 12 not taken.
✓ Branch 4 → 5 taken 1795 times.
✓ Branch 4 → 8 taken 1403 times.
✓ Branch 5 → 6 taken 1795 times.
✗ Branch 5 → 12 not taken.
✓ Branch 6 → 7 taken 212 times.
✓ Branch 6 → 8 taken 1583 times.
3198 return BUILTIN_FUNCTIONS_MAP.contains(fqFunctionName) && data.at(manIdx).compileTimeValueSet;
528 }
529
530 218 CompileTimeValue FctCallNode::getCompileTimeValue(size_t manIdx) const { return data.at(manIdx).compileTimeValue; }
531
532 22 void FctCallNode::setCompileTimeValue(const CompileTimeValue &value, size_t manIdx) {
533 22 data.at(manIdx).setCompileTimeValue(value);
534 22 }
535
536 6110 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 6110 times.
✗ Branch 3 → 14 not taken.
6110 const auto it = BUILTIN_FUNCTIONS_MAP.find(fqFunctionName);
539
4/4
✓ Branch 6 → 7 taken 1785 times.
✓ Branch 6 → 10 taken 4325 times.
✓ Branch 8 → 9 taken 1003 times.
✓ Branch 8 → 10 taken 782 times.
6110 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 10837 bool FctCallNode::hasReturnValueReceiver() const {
548 10837 const ASTNode *node = parent;
549
2/2
✓ Branch 13 → 3 taken 37089 times.
✓ Branch 13 → 14 taken 1143 times.
38232 while (!node->isAssignExpr()) {
550
2/2
✓ Branch 4 → 5 taken 342 times.
✓ Branch 4 → 6 taken 36747 times.
37089 if (node->isExprStmt())
551 342 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 36747 times.
✗ Branch 6 → 26 not taken.
✓ Branch 9 → 10 taken 9352 times.
✓ Branch 9 → 11 taken 27395 times.
36747 if (node->getChildren().size() > 1)
554 9352 return true;
555 27395 node = node->parent;
556 }
557 // Also check the condition of the assign expression
558
3/12
✓ Branch 14 → 15 taken 1143 times.
✗ Branch 14 → 27 not taken.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 19 taken 1143 times.
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 27 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 20 not taken.
✓ Branch 22 → 23 taken 1143 times.
✗ Branch 22 → 24 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
1143 return node->getChildren().size() > 1 || !node->parent->isExprStmt();
559 }
560
561 16 bool LambdaFuncNode::returnsOnAllControlPaths(bool *overrideUnreachable, size_t manIdx) const {
562 16 return body->returnsOnAllControlPaths(overrideUnreachable, manIdx);
563 }
564
565 21 bool LambdaProcNode::returnsOnAllControlPaths(bool *overrideUnreachable, size_t manIdx) const {
566 21 return body->returnsOnAllControlPaths(overrideUnreachable, manIdx);
567 }
568
569 1839 void DataTypeNode::setFieldTypeRecursive() { // NOLINT(*-no-recursion)
570 // Set the current node to field type
571 1839 isFieldType = true;
572 // Do the same for all template nodes
573
4/4
✓ Branch 2 → 3 taken 836 times.
✓ Branch 2 → 12 taken 1003 times.
✓ Branch 3 → 4 taken 181 times.
✓ Branch 3 → 12 taken 655 times.
1839 if (const CustomDataTypeNode *customType = baseDataType->customDataType; customType != nullptr && customType->templateTypeLst)
574
2/2
✓ Branch 10 → 6 taken 252 times.
✓ Branch 10 → 11 taken 181 times.
433 for (DataTypeNode *templateNode : customType->templateTypeLst->dataTypes)
575
1/2
✓ Branch 7 → 8 taken 252 times.
✗ Branch 7 → 13 not taken.
252 templateNode->setFieldTypeRecursive();
576 1839 }
577
578 } // namespace spice::compiler
579