GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 59.5% 207 / 0 / 348
Functions: 84.5% 60 / 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 <ANTLRInputStream.h>
6
7 #include <SourceFile.h>
8 #include <ast/Attributes.h>
9 #include <exception/SemanticError.h>
10 #include <symboltablebuilder/SymbolTableBuilder.h>
11 #include <typechecker/BuiltinFunctions.h>
12
13 namespace spice::compiler {
14
15 // Constant definitions
16 static constexpr size_t ERROR_MESSAGE_CONTEXT = 20;
17
18 5551 std::string ASTNode::getErrorMessage() const {
19 5551 antlr4::CharStream *inputStream = codeLoc.sourceFile->antlrCtx.inputStream.get();
20 5551 const antlr4::misc::Interval &sourceInterval = codeLoc.sourceInterval;
21 5551 antlr4::misc::Interval extSourceInterval(sourceInterval);
22
23 // If we have a multi-line interval, only use the first line
24
3/4
✓ Branch 3 → 4 taken 5551 times.
✗ Branch 3 → 77 not taken.
✓ Branch 6 → 7 taken 38 times.
✓ Branch 6 → 8 taken 5513 times.
5551 if (const size_t offset = inputStream->getText(extSourceInterval).find('\n'); offset != std::string::npos)
25 38 extSourceInterval.b = extSourceInterval.a + static_cast<ssize_t>(offset);
26
27 5551 size_t markerIndentation = 0;
28
2/2
✓ Branch 20 → 9 taken 66698 times.
✓ Branch 20 → 21 taken 1741 times.
68439 for (; markerIndentation < ERROR_MESSAGE_CONTEXT; markerIndentation++) {
29 66698 extSourceInterval.a--;
30
9/12
✓ Branch 9 → 10 taken 66666 times.
✓ Branch 9 → 13 taken 32 times.
✓ Branch 10 → 11 taken 66666 times.
✗ Branch 10 → 78 not taken.
✓ Branch 12 → 13 taken 3778 times.
✓ Branch 12 → 14 taken 62888 times.
✓ Branch 15 → 16 taken 66666 times.
✓ Branch 15 → 17 taken 32 times.
✓ Branch 17 → 18 taken 3810 times.
✓ Branch 17 → 19 taken 62888 times.
✗ Branch 78 → 79 not taken.
✗ Branch 78 → 80 not taken.
66698 if (extSourceInterval.a < 0 || inputStream->getText(extSourceInterval).find('\n') != std::string::npos) {
31 3810 extSourceInterval.a++;
32 3810 break;
33 }
34 }
35
2/2
✓ Branch 34 → 22 taken 17634 times.
✓ Branch 34 → 35 taken 120 times.
17754 for (size_t suffixContext = 0; suffixContext < ERROR_MESSAGE_CONTEXT; suffixContext++) {
36 17634 extSourceInterval.b++;
37
4/6
✓ Branch 22 → 23 taken 17634 times.
✗ Branch 22 → 82 not taken.
✓ Branch 23 → 24 taken 17634 times.
✗ Branch 23 → 27 not taken.
✓ Branch 26 → 27 taken 5431 times.
✓ Branch 26 → 28 taken 12203 times.
35268 if (static_cast<size_t>(extSourceInterval.b) > inputStream->size() ||
38
4/8
✓ Branch 24 → 25 taken 17634 times.
✗ Branch 24 → 82 not taken.
✓ Branch 29 → 30 taken 17634 times.
✗ Branch 29 → 31 not taken.
✓ Branch 31 → 32 taken 5431 times.
✓ Branch 31 → 33 taken 12203 times.
✗ Branch 82 → 83 not taken.
✗ Branch 82 → 84 not taken.
35268 inputStream->getText(extSourceInterval).find('\n') != std::string::npos) {
39 5431 extSourceInterval.b--;
40 5431 break;
41 }
42 }
43
44 // Trim start
45
3/4
✓ Branch 37 → 38 taken 33110 times.
✗ Branch 37 → 86 not taken.
✓ Branch 40 → 36 taken 27559 times.
✓ Branch 40 → 41 taken 5551 times.
33110 while (inputStream->getText(extSourceInterval)[0] == ' ') {
46 27559 extSourceInterval.a++;
47 27559 markerIndentation--;
48 }
49
50 // Trim end
51
3/4
✓ Branch 41 → 42 taken 5551 times.
✗ Branch 41 → 87 not taken.
✓ Branch 45 → 46 taken 38 times.
✓ Branch 45 → 47 taken 5513 times.
5551 if (inputStream->getText(extSourceInterval)[extSourceInterval.length() - 1] == '\n')
52 38 extSourceInterval.b--;
53
54 5551 const std::string lineNumberStr = std::to_string(codeLoc.line);
55 5551 markerIndentation += lineNumberStr.length() + 2;
56
57 // Build error message
58
1/2
✓ Branch 49 → 50 taken 5551 times.
✗ Branch 49 → 107 not taken.
5551 std::stringstream ss;
59
5/10
✓ Branch 50 → 51 taken 5551 times.
✗ Branch 50 → 105 not taken.
✓ Branch 51 → 52 taken 5551 times.
✗ Branch 51 → 105 not taken.
✓ Branch 52 → 53 taken 5551 times.
✗ Branch 52 → 90 not taken.
✓ Branch 53 → 54 taken 5551 times.
✗ Branch 53 → 88 not taken.
✓ Branch 54 → 55 taken 5551 times.
✗ Branch 54 → 88 not taken.
5551 ss << lineNumberStr << " " << inputStream->getText(extSourceInterval) << "\n";
60
2/4
✓ Branch 58 → 59 taken 5551 times.
✗ Branch 58 → 93 not taken.
✓ Branch 59 → 60 taken 5551 times.
✗ Branch 59 → 91 not taken.
11102 ss << std::string(markerIndentation, ' ');
61
2/4
✓ Branch 67 → 68 taken 5551 times.
✗ Branch 67 → 99 not taken.
✓ Branch 68 → 69 taken 5551 times.
✗ Branch 68 → 97 not taken.
5551 ss << std::string(std::min(sourceInterval.length(), extSourceInterval.length()), '^');
62
1/2
✓ Branch 71 → 72 taken 5551 times.
✗ Branch 71 → 105 not taken.
11102 return ss.str();
63 5551 }
64
65 262863 const StmtLstNode *ASTNode::getNextOuterStmtLst() const { // NOLINT(*-no-recursion)
66
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 262863 times.
262863 assert(parent != nullptr);
67
2/2
✓ Branch 5 → 6 taken 122850 times.
✓ Branch 5 → 16 taken 140013 times.
385713 return isStmtLst() ? spice_pointer_cast<const StmtLstNode *>(this) : parent->getNextOuterStmtLst();
68 }
69
70 96 std::string ASTNode::getEnclosingFunctionSignature(size_t manIdx) const { // NOLINT(*-no-recursion)
71
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 96 times.
96 assert(parent != nullptr);
72
2/2
✓ Branch 5 → 6 taken 16 times.
✓ Branch 5 → 7 taken 80 times.
96 return isFctOrProcDef() ? getFunctionSignature(manIdx) : parent->getEnclosingFunctionSignature(manIdx);
73 }
74
75 1174 bool MainFctDefNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
76 1174 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
77 }
78
79 78316 bool FctDefBaseNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
80 78316 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
81 }
82
83 825 CompileTimeValue GlobalVarDefNode::getCompileTimeValue(size_t manIdx) const { return constant->getCompileTimeValue(manIdx); }
84
85 5607 bool ForLoopNode::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 2 times.
✓ Branch 3 → 7 taken 5605 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 2 times.
5607 const bool condAlwaysTrue = condAssign->hasCompileTimeValue(manIdx) && condAssign->getCompileTimeValue(manIdx).boolValue;
89
1/4
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 12 taken 5607 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
5607 return condAlwaysTrue && body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
90 }
91
92 2855 bool WhileLoopNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
93 // If we have the guarantee that the loop condition is always true and the loop body returns on all control paths,
94 // we can assume that the loop itself will always return
95
3/4
✓ Branch 3 → 4 taken 116 times.
✓ Branch 3 → 7 taken 2739 times.
✓ Branch 5 → 6 taken 116 times.
✗ Branch 5 → 7 not taken.
2855 const bool condAlwaysTrue = condition->hasCompileTimeValue(manIdx) && condition->getCompileTimeValue(manIdx).boolValue;
96
4/4
✓ Branch 8 → 9 taken 116 times.
✓ Branch 8 → 12 taken 2739 times.
✓ Branch 10 → 11 taken 18 times.
✓ Branch 10 → 12 taken 98 times.
2855 return condAlwaysTrue && body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
97 }
98
99 33 bool DoWhileLoopNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
100 // Do-while loops will always be executed at least once. So if the body returns on all control paths, the loop will as well
101 33 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
102 }
103
104 31350 bool IfStmtNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const { // NOLINT(misc-no-recursion)
105 // If the condition always evaluates to 'true' the then block must return
106
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 31350 times.
31350 if (!doCompileElseBranch(manIdx))
107 return thenBody->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
108
109 // If the condition always evaluates to 'false' the else block must return
110
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 14 taken 31350 times.
31350 if (!doCompileThenBranch(manIdx))
111 return elseStmt != nullptr && elseStmt->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
112
113 // If the condition does not always evaluate to 'true' or 'false' we need to check both branches
114
4/4
✓ Branch 15 → 16 taken 22794 times.
✓ Branch 15 → 20 taken 8556 times.
✓ Branch 16 → 17 taken 293 times.
✓ Branch 16 → 20 taken 22501 times.
31643 return thenBody->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx) && elseStmt != nullptr &&
115
2/2
✓ Branch 18 → 19 taken 247 times.
✓ Branch 18 → 20 taken 46 times.
31643 elseStmt->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
116 }
117
118 293 bool ElseStmtNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable,
119 size_t manIdx) const { // NOLINT(misc-no-recursion)
120
2/2
✓ Branch 2 → 3 taken 104 times.
✓ Branch 2 → 5 taken 189 times.
293 if (isElseIf)
121 104 return ifStmt->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
122 189 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
123 }
124
125 188 bool SwitchStmtNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
126 1202 const auto pred = [=](const CaseBranchNode *node) {
127 1014 return node->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
128 188 };
129
1/2
✓ Branch 2 → 3 taken 188 times.
✗ Branch 2 → 14 not taken.
188 const bool allCaseBranchesReturn = std::ranges::all_of(caseBranches, pred);
130 const bool defaultBranchReturns =
131
5/6
✓ Branch 3 → 4 taken 156 times.
✓ Branch 3 → 6 taken 32 times.
✓ Branch 4 → 5 taken 156 times.
✗ Branch 4 → 14 not taken.
✓ Branch 5 → 6 taken 134 times.
✓ Branch 5 → 7 taken 22 times.
188 !defaultBranch || defaultBranch->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
132
3/4
✓ Branch 8 → 9 taken 118 times.
✓ Branch 8 → 11 taken 70 times.
✓ Branch 9 → 10 taken 118 times.
✗ Branch 9 → 11 not taken.
188 return allCaseBranchesReturn && defaultBranchReturns;
133 }
134
135 1014 bool CaseBranchNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
136 1014 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
137 }
138
139 156 bool DefaultBranchNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
140 156 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
141 }
142
143 128058 bool StmtLstNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
144 // An empty statement list does not return at all
145
2/2
✓ Branch 3 → 4 taken 415 times.
✓ Branch 3 → 5 taken 127643 times.
128058 if (statements.empty())
146 415 return false;
147 // A statement list returns on all control paths, if the one direct child statement returns on all control paths
148 127643 bool returnsOnAllControlPaths = false;
149
2/2
✓ Branch 26 → 7 taken 250142 times.
✓ Branch 26 → 27 taken 127643 times.
505428 for (StmtNode *child : statements) {
150
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 250142 times.
250142 assert(child != nullptr);
151
152 // Prevent marking instructions as unreachable if doSetPredecessorsUnreachable is set to false
153
4/4
✓ Branch 11 → 12 taken 4670 times.
✓ Branch 11 → 14 taken 245472 times.
✓ Branch 12 → 13 taken 6 times.
✓ Branch 12 → 14 taken 4664 times.
250142 if (returnsOnAllControlPaths && *doSetPredecessorsUnreachable)
154 6 child->unreachable = true;
155
156
3/4
✓ Branch 14 → 15 taken 250142 times.
✗ Branch 14 → 29 not taken.
✓ Branch 15 → 16 taken 78973 times.
✓ Branch 15 → 17 taken 171169 times.
250142 if (child->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx))
157 78973 returnsOnAllControlPaths = true;
158 }
159 127643 return returnsOnAllControlPaths;
160 }
161
162 14414 std::vector<const CompileTimeValue *> AttrLstNode::getAttrValuesByName(const std::string &key) const {
163
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 14414 times.
14414 assert(ATTR_CONFIGS.contains(key));
164
165 14414 std::vector<const CompileTimeValue *> attributeValues;
166
2/2
✓ Branch 28 → 7 taken 25070 times.
✓ Branch 28 → 29 taken 14414 times.
53898 for (const AttrNode *attrNode : attributes) {
167 // Skip attributes with different keys
168
2/2
✓ Branch 10 → 11 taken 18810 times.
✓ Branch 10 → 12 taken 6260 times.
25070 if (attrNode->key != key)
169 18810 continue;
170
171 // Found a matching attribute
172 6260 const CompileTimeValue *value = attrNode->getValue();
173
2/2
✓ Branch 13 → 14 taken 514 times.
✓ Branch 13 → 16 taken 5746 times.
6260 if (!value) {
174 // If the attribute has no value, we use the default value
175
1/2
✓ Branch 14 → 15 taken 514 times.
✗ Branch 14 → 31 not taken.
514 attributeValues.push_back(&DEFAULT_BOOL_COMPILE_VALUE);
176 } else {
177 // If the attribute has a value, we use the value
178
1/2
✓ Branch 16 → 17 taken 5746 times.
✗ Branch 16 → 32 not taken.
5746 attributeValues.push_back(value);
179 }
180 }
181
182 14414 return attributeValues;
183 }
184
185 7928 const CompileTimeValue *AttrLstNode::getAttrValueByName(const std::string &key) const {
186
1/2
✓ Branch 2 → 3 taken 7928 times.
✗ Branch 2 → 12 not taken.
7928 const std::vector<const CompileTimeValue *> attrs = getAttrValuesByName(key);
187
2/2
✓ Branch 4 → 5 taken 4684 times.
✓ Branch 4 → 6 taken 3244 times.
15856 return attrs.empty() ? nullptr : attrs.back();
188 7928 }
189
190 12210 bool AttrLstNode::hasAttr(const std::string &key) const {
191 37546 return std::ranges::any_of(attributes, [&](const AttrNode *attr) { return attr->key == key; });
192 }
193
194
2/2
✓ Branch 2 → 3 taken 5746 times.
✓ Branch 2 → 4 taken 514 times.
6260 const CompileTimeValue *AttrNode::getValue() const { return value ? &value->compileTimeValue : nullptr; }
195
196 40928 bool AssignExprNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
197 // If it's a ternary, do the default thing
198
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 40928 times.
40928 if (op == AssignOp::OP_NONE)
199 return ternaryExpr->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
200
201 // If it's a modification on the result variable, we technically return from the function, but at the end of the function.
202 40928 const AtomicExprNode *atomicExpr = getLhsAtomicNode();
203
6/6
✓ Branch 6 → 7 taken 40453 times.
✓ Branch 6 → 10 taken 475 times.
✓ Branch 8 → 9 taken 3950 times.
✓ Branch 8 → 10 taken 36503 times.
✓ Branch 11 → 12 taken 3950 times.
✓ Branch 11 → 13 taken 36978 times.
40928 if (atomicExpr && atomicExpr->fqIdentifier == RETURN_VARIABLE_NAME) {
204 // If we assign the result variable, we technically return from the function, but at the end of the function.
205 // Therefore, the following code is not unreachable, but will be executed in any case.
206 3950 *doSetPredecessorsUnreachable = false;
207 3950 return true;
208 }
209
210 36978 return false;
211 }
212
213 40928 AtomicExprNode *AssignExprNode::getLhsAtomicNode() const {
214
3/4
✓ Branch 2 → 3 taken 40928 times.
✗ Branch 2 → 4 not taken.
✓ Branch 5 → 6 taken 8323 times.
✓ Branch 5 → 7 taken 32605 times.
40928 if (auto *atomicNode = dynamic_cast<AtomicExprNode *>(lhs))
215 8323 return atomicNode;
216
4/6
✓ Branch 7 → 8 taken 32605 times.
✗ Branch 7 → 28 not taken.
✓ Branch 9 → 10 taken 32605 times.
✗ Branch 9 → 11 not taken.
✓ Branch 13 → 14 taken 30631 times.
✓ Branch 13 → 15 taken 1974 times.
32605 if (auto *atomicNode = dynamic_cast<AtomicExprNode *>(lhs->getChildren().back()))
217 30631 return atomicNode;
218
5/8
✓ Branch 15 → 16 taken 1974 times.
✗ Branch 15 → 31 not taken.
✓ Branch 17 → 18 taken 1974 times.
✗ Branch 17 → 29 not taken.
✓ Branch 19 → 20 taken 1974 times.
✗ Branch 19 → 21 not taken.
✓ Branch 24 → 25 taken 1499 times.
✓ Branch 24 → 26 taken 475 times.
1974 if (auto *atomicNode = dynamic_cast<AtomicExprNode *>(lhs->getChildren().back()->getChildren().front()))
219 1499 return atomicNode;
220 475 return nullptr;
221 }
222
223 14 bool TernaryExprNode::hasCompileTimeValue(size_t manIdx) const {
224
3/4
✓ Branch 2 → 3 taken 14 times.
✗ Branch 2 → 5 not taken.
✓ Branch 4 → 5 taken 8 times.
✓ Branch 4 → 6 taken 6 times.
14 const bool trueExprHasCompileTimeValue = !trueExpr || trueExpr->hasCompileTimeValue(manIdx);
225
3/4
✓ Branch 7 → 8 taken 14 times.
✗ Branch 7 → 10 not taken.
✓ Branch 9 → 10 taken 10 times.
✓ Branch 9 → 11 taken 4 times.
14 const bool falseExprHasCompileTimeValue = !falseExpr || falseExpr->hasCompileTimeValue(manIdx);
226
1/6
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 17 taken 14 times.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 17 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
14 return condition->hasCompileTimeValue(manIdx) && trueExprHasCompileTimeValue && falseExprHasCompileTimeValue;
227 }
228
229 CompileTimeValue TernaryExprNode::getCompileTimeValue(size_t manIdx) const {
230 assert(condition != nullptr);
231 if (!trueExpr && !falseExpr)
232 return condition->getCompileTimeValue(manIdx);
233
234 // If the condition has no compile time value, we do not need to evaluate the true and false values
235 if (!condition->hasCompileTimeValue(manIdx))
236 return {};
237
238 // Check if the condition always evaluates to 'true'
239 if (condition->getCompileTimeValue(manIdx).boolValue) {
240 const ExprNode *trueValue = isShortened ? condition : trueExpr;
241 assert(trueValue != nullptr);
242 return trueValue->getCompileTimeValue(manIdx);
243 }
244
245 assert(falseExpr != nullptr);
246 return falseExpr->getCompileTimeValue(manIdx);
247 }
248
249 2214 bool LogicalOrExprNode::hasCompileTimeValue(size_t manIdx) const {
250 4428 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
251 }
252
253 CompileTimeValue LogicalOrExprNode::getCompileTimeValue(size_t manIdx) const {
254 if (operands.size() == 1)
255 return operands.front()->getCompileTimeValue(manIdx);
256
257 // Check if one expression evaluates to 'true'
258 for (const ExprNode *op : operands) {
259 assert(op->hasCompileTimeValue(manIdx));
260 // If one operand evaluates to 'true' the whole expression is 'true'
261 if (const CompileTimeValue opCompileTimeValue = op->getCompileTimeValue(manIdx); opCompileTimeValue.boolValue)
262 return CompileTimeValue{.boolValue = true};
263 }
264
265 // Return 'false'
266 return CompileTimeValue{.boolValue = false};
267 }
268
269 2288 bool LogicalAndExprNode::hasCompileTimeValue(size_t manIdx) const {
270 4576 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
271 }
272
273 CompileTimeValue LogicalAndExprNode::getCompileTimeValue(size_t manIdx) const {
274 if (operands.size() == 1)
275 return operands.front()->getCompileTimeValue(manIdx);
276
277 // Check if all expressions evaluate to 'true'
278 for (const ExprNode *op : operands) {
279 assert(op->hasCompileTimeValue(manIdx));
280 // If one operand evaluates to 'false' the whole expression is 'false'
281 if (const CompileTimeValue opCompileTimeValue = op->getCompileTimeValue(manIdx); !opCompileTimeValue.boolValue)
282 return CompileTimeValue{.boolValue = false};
283 }
284
285 // Return 'false'
286 return CompileTimeValue{.boolValue = false};
287 }
288
289 490 bool BitwiseOrExprNode::hasCompileTimeValue(size_t manIdx) const {
290 980 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
291 }
292
293 CompileTimeValue BitwiseOrExprNode::getCompileTimeValue(size_t manIdx) const {
294 if (operands.size() == 1)
295 return operands.front()->getCompileTimeValue(manIdx);
296
297 CompileTimeValue result = operands.front()->getCompileTimeValue(manIdx);
298 for (size_t i = 1; i < operands.size(); i++) {
299 assert(operands.at(i)->hasCompileTimeValue(manIdx));
300 const CompileTimeValue opCompileTimeValue = operands.at(i)->getCompileTimeValue(manIdx);
301 result.longValue |= opCompileTimeValue.longValue;
302 }
303
304 return result;
305 }
306
307 2 bool BitwiseXorExprNode::hasCompileTimeValue(size_t manIdx) const {
308 4 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
309 }
310
311 CompileTimeValue BitwiseXorExprNode::getCompileTimeValue(size_t manIdx) const {
312 if (operands.size() == 1)
313 return operands.front()->getCompileTimeValue(manIdx);
314
315 CompileTimeValue result = operands.front()->getCompileTimeValue(manIdx);
316 for (size_t i = 1; i < operands.size(); i++) {
317 assert(operands.at(i)->hasCompileTimeValue(manIdx));
318 const CompileTimeValue opCompileTimeValue = operands.at(i)->getCompileTimeValue(manIdx);
319 result.longValue ^= opCompileTimeValue.longValue;
320 }
321
322 return result;
323 }
324
325 32 bool BitwiseAndExprNode::hasCompileTimeValue(size_t manIdx) const {
326 64 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
327 }
328
329 CompileTimeValue BitwiseAndExprNode::getCompileTimeValue(size_t manIdx) const {
330 if (operands.size() == 1)
331 return operands.front()->getCompileTimeValue(manIdx);
332
333 CompileTimeValue result = operands.front()->getCompileTimeValue(manIdx);
334 for (size_t i = 1; i < operands.size(); i++) {
335 assert(operands.at(i)->hasCompileTimeValue(manIdx));
336 const CompileTimeValue opCompileTimeValue = operands.at(i)->getCompileTimeValue(manIdx);
337 result.longValue &= opCompileTimeValue.longValue;
338 }
339
340 return result;
341 }
342
343 31654 bool EqualityExprNode::hasCompileTimeValue(size_t manIdx) const {
344 63344 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
345 }
346
347 38 CompileTimeValue EqualityExprNode::getCompileTimeValue(size_t manIdx) const {
348
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 38 times.
38 if (operands.size() == 1)
349 return operands.front()->getCompileTimeValue(manIdx);
350
351
2/4
✓ Branch 7 → 8 taken 38 times.
✗ Branch 7 → 34 not taken.
✓ Branch 8 → 9 taken 38 times.
✗ Branch 8 → 34 not taken.
38 const CompileTimeValue op0Value = operands.at(0)->getCompileTimeValue(manIdx);
352
2/4
✓ Branch 9 → 10 taken 38 times.
✗ Branch 9 → 34 not taken.
✓ Branch 10 → 11 taken 38 times.
✗ Branch 10 → 34 not taken.
38 const CompileTimeValue op1Value = operands.at(1)->getCompileTimeValue(manIdx);
353
2/2
✓ Branch 11 → 12 taken 36 times.
✓ Branch 11 → 13 taken 2 times.
38 if (op == EqualityOp::OP_EQUAL)
354 36 return CompileTimeValue{.boolValue = op0Value.longValue == op1Value.longValue};
355
1/2
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 15 not taken.
2 if (op == EqualityOp::OP_NOT_EQUAL)
356 2 return CompileTimeValue{.boolValue = op0Value.longValue != op1Value.longValue};
357
358 throw CompilerError(UNHANDLED_BRANCH, "EqualityExprNode::getCompileTimeValue()");
359 }
360
361 19871 bool RelationalExprNode::hasCompileTimeValue(size_t manIdx) const {
362 39806 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
363 }
364
365 82 CompileTimeValue RelationalExprNode::getCompileTimeValue(size_t manIdx) const {
366
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 82 times.
82 if (operands.size() == 1)
367 return operands.front()->getCompileTimeValue(manIdx);
368
369
2/4
✓ Branch 7 → 8 taken 82 times.
✗ Branch 7 → 38 not taken.
✓ Branch 8 → 9 taken 82 times.
✗ Branch 8 → 38 not taken.
82 const CompileTimeValue op0Value = operands.at(0)->getCompileTimeValue(manIdx);
370
2/4
✓ Branch 9 → 10 taken 82 times.
✗ Branch 9 → 38 not taken.
✓ Branch 10 → 11 taken 82 times.
✗ Branch 10 → 38 not taken.
82 const CompileTimeValue op1Value = operands.at(1)->getCompileTimeValue(manIdx);
371
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 82 times.
82 if (op == RelationalOp::OP_LESS)
372 return CompileTimeValue{.boolValue = op0Value.longValue < op1Value.longValue};
373
2/2
✓ Branch 13 → 14 taken 80 times.
✓ Branch 13 → 15 taken 2 times.
82 if (op == RelationalOp::OP_GREATER)
374 80 return CompileTimeValue{.boolValue = op0Value.longValue > op1Value.longValue};
375
1/2
✓ Branch 15 → 16 taken 2 times.
✗ Branch 15 → 17 not taken.
2 if (op == RelationalOp::OP_LESS_EQUAL)
376 2 return CompileTimeValue{.boolValue = op0Value.longValue <= op1Value.longValue};
377 if (op == RelationalOp::OP_GREATER_EQUAL)
378 return CompileTimeValue{.boolValue = op0Value.longValue >= op1Value.longValue};
379
380 throw CompilerError(UNHANDLED_BRANCH, "RelationalExprNode::getCompileTimeValue()");
381 }
382
383 8 bool ShiftExprNode::hasCompileTimeValue(size_t manIdx) const {
384 16 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
385 }
386
387 CompileTimeValue ShiftExprNode::getCompileTimeValue(size_t manIdx) const {
388 if (operands.size() == 1)
389 return operands.front()->getCompileTimeValue(manIdx);
390
391 CompileTimeValue result = operands.front()->getCompileTimeValue(manIdx);
392 OpQueue opQueueCopy = opQueue;
393 for (size_t i = 1; i < operands.size(); i++) {
394 assert(operands.at(i)->hasCompileTimeValue(manIdx));
395 const CompileTimeValue opCompileTimeValue = operands.at(i)->getCompileTimeValue(manIdx);
396 const ShiftOp op = opQueueCopy.front().first;
397 opQueueCopy.pop();
398 if (op == ShiftOp::OP_SHIFT_LEFT)
399 result.longValue <<= opCompileTimeValue.longValue;
400 else if (op == ShiftOp::OP_SHIFT_RIGHT)
401 result.longValue >>= opCompileTimeValue.longValue;
402 else
403 throw CompilerError(UNHANDLED_BRANCH, "ShiftExprNode::getCompileTimeValue()");
404 }
405 return result;
406 }
407
408 1092 bool AdditiveExprNode::hasCompileTimeValue(size_t manIdx) const {
409 2188 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
410 }
411
412 CompileTimeValue AdditiveExprNode::getCompileTimeValue(size_t manIdx) const {
413 if (operands.size() == 1)
414 return operands.front()->getCompileTimeValue(manIdx);
415
416 CompileTimeValue result = operands.front()->getCompileTimeValue(manIdx);
417 OpQueue opQueueCopy = opQueue;
418 for (size_t i = 1; i < operands.size(); i++) {
419 assert(operands.at(i)->hasCompileTimeValue(manIdx));
420 const CompileTimeValue opCompileTimeValue = operands.at(i)->getCompileTimeValue(manIdx);
421 const AdditiveOp op = opQueueCopy.front().first;
422 opQueueCopy.pop();
423 if (op == AdditiveOp::OP_PLUS)
424 result.longValue += opCompileTimeValue.longValue;
425 else if (op == AdditiveOp::OP_MINUS)
426 result.longValue -= opCompileTimeValue.longValue;
427 else
428 throw CompilerError(UNHANDLED_BRANCH, "AdditiveExprNode::getCompileTimeValue()");
429 }
430 return result;
431 }
432
433 1054 bool MultiplicativeExprNode::hasCompileTimeValue(size_t manIdx) const {
434 2110 return std::ranges::all_of(operands, [=](const ExprNode *node) { return node->hasCompileTimeValue(manIdx); });
435 }
436
437 CompileTimeValue MultiplicativeExprNode::getCompileTimeValue(size_t manIdx) const {
438 if (operands.size() == 1)
439 return operands.front()->getCompileTimeValue(manIdx);
440
441 CompileTimeValue result = operands.front()->getCompileTimeValue(manIdx);
442 OpQueue opQueueCopy = opQueue;
443 for (size_t i = 1; i < operands.size(); i++) {
444 assert(operands.at(i)->hasCompileTimeValue(manIdx));
445 const CompileTimeValue opCompileTimeValue = operands.at(i)->getCompileTimeValue(manIdx);
446 const MultiplicativeOp op = opQueueCopy.front().first;
447 opQueueCopy.pop();
448 if (op == MultiplicativeOp::OP_MUL) {
449 result.longValue *= opCompileTimeValue.longValue;
450 } else if (op == MultiplicativeOp::OP_DIV) {
451 if (opCompileTimeValue.longValue == 0)
452 throw SemanticError(operands.at(i), DIVISION_BY_ZERO, "Dividing by zero is not allowed.");
453 result.longValue /= opCompileTimeValue.longValue;
454 } else if (op == MultiplicativeOp::OP_REM) {
455 result.longValue %= opCompileTimeValue.longValue;
456 } else {
457 throw CompilerError(UNHANDLED_BRANCH, "MultiplicativeExprNode::getCompileTimeValue()");
458 }
459 }
460 return result;
461 }
462
463 1316 bool CastExprNode::hasCompileTimeValue(size_t manIdx) const {
464
1/2
✓ Branch 2 → 3 taken 1316 times.
✗ Branch 2 → 5 not taken.
1316 return isCast ? assignExpr->hasCompileTimeValue(manIdx) : prefixUnaryExpr->hasCompileTimeValue(manIdx);
465 }
466
467 CompileTimeValue CastExprNode::getCompileTimeValue(size_t manIdx) const {
468 return isCast ? assignExpr->getCompileTimeValue(manIdx) : prefixUnaryExpr->getCompileTimeValue(manIdx);
469 }
470
471 6530 bool PrefixUnaryExprNode::hasCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
472
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 6530 times.
6530 if (postfixUnaryExpr)
473 return postfixUnaryExpr->hasCompileTimeValue(manIdx);
474
475
3/4
✓ Branch 6 → 7 taken 6448 times.
✓ Branch 6 → 11 taken 82 times.
✓ Branch 7 → 8 taken 6448 times.
✗ Branch 7 → 11 not taken.
6530 const bool isSupported = op == PrefixUnaryOp::OP_NONE || op == PrefixUnaryOp::OP_MINUS || op == PrefixUnaryOp::OP_PLUS_PLUS ||
476
4/6
✓ Branch 5 → 6 taken 6530 times.
✗ Branch 5 → 11 not taken.
✓ Branch 8 → 9 taken 6448 times.
✗ Branch 8 → 11 not taken.
✓ Branch 9 → 10 taken 177 times.
✓ Branch 9 → 11 taken 6271 times.
13237 op == PrefixUnaryOp::OP_MINUS_MINUS || op == PrefixUnaryOp::OP_NOT ||
477
2/2
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 12 taken 175 times.
177 op == PrefixUnaryOp::OP_BITWISE_NOT;
478
4/4
✓ Branch 13 → 14 taken 6355 times.
✓ Branch 13 → 17 taken 175 times.
✓ Branch 15 → 16 taken 638 times.
✓ Branch 15 → 17 taken 5717 times.
6530 return isSupported && prefixUnaryExpr->hasCompileTimeValue(manIdx);
479 }
480
481 1276 CompileTimeValue PrefixUnaryExprNode::getCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
482
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 1276 times.
1276 if (postfixUnaryExpr)
483 return postfixUnaryExpr->getCompileTimeValue(manIdx);
484
485
1/2
✓ Branch 5 → 6 taken 1276 times.
✗ Branch 5 → 35 not taken.
1276 CompileTimeValue opValue = prefixUnaryExpr->getCompileTimeValue(manIdx);
486
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 1276 times.
1276 if (op == PrefixUnaryOp::OP_MINUS)
487 return CompileTimeValue{.longValue = -opValue.longValue};
488
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 1276 times.
1276 if (op == PrefixUnaryOp::OP_PLUS_PLUS)
489 return CompileTimeValue{.longValue = ++opValue.longValue};
490
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 1276 times.
1276 if (op == PrefixUnaryOp::OP_MINUS_MINUS)
491 return CompileTimeValue{.longValue = --opValue.longValue};
492
1/2
✓ Branch 12 → 13 taken 1276 times.
✗ Branch 12 → 14 not taken.
1276 if (op == PrefixUnaryOp::OP_NOT)
493 1276 return CompileTimeValue{.boolValue = !opValue.boolValue};
494 if (op == PrefixUnaryOp::OP_BITWISE_NOT)
495 return CompileTimeValue{.longValue = ~opValue.longValue};
496
497 throw CompilerError(UNHANDLED_BRANCH, "PrefixUnaryExprNode::getCompileTimeValue()");
498 }
499
500 18862 bool PostfixUnaryExprNode::hasCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
501
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 18862 times.
18862 if (atomicExpr)
502 return atomicExpr->hasCompileTimeValue(manIdx);
503
504 18862 const bool isSupported =
505
4/6
✓ Branch 5 → 6 taken 18862 times.
✗ Branch 5 → 8 not taken.
✓ Branch 6 → 7 taken 18862 times.
✗ Branch 6 → 8 not taken.
✓ Branch 7 → 8 taken 6 times.
✓ Branch 7 → 9 taken 18856 times.
18862 op == PostfixUnaryOp::OP_NONE || op == PostfixUnaryOp::OP_PLUS_PLUS || op == PostfixUnaryOp::OP_MINUS_MINUS;
506
3/4
✓ Branch 10 → 11 taken 6 times.
✓ Branch 10 → 14 taken 18856 times.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 6 times.
18862 return isSupported && postfixUnaryExpr->hasCompileTimeValue(manIdx);
507 }
508
509 CompileTimeValue PostfixUnaryExprNode::getCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
510 if (atomicExpr)
511 return atomicExpr->getCompileTimeValue(manIdx);
512
513 CompileTimeValue opValue = postfixUnaryExpr->getCompileTimeValue(manIdx);
514 if (op == PostfixUnaryOp::OP_PLUS_PLUS)
515 return CompileTimeValue{.longValue = opValue.longValue++};
516 if (op == PostfixUnaryOp::OP_MINUS_MINUS)
517 return CompileTimeValue{.longValue = opValue.longValue--};
518
519 throw CompilerError(UNHANDLED_BRANCH, "PostfixUnaryExprNode::getCompileTimeValue()");
520 }
521
522
4/4
✓ Branch 2 → 3 taken 15718 times.
✓ Branch 2 → 5 taken 20 times.
✓ Branch 4 → 5 taken 756 times.
✓ Branch 4 → 6 taken 14962 times.
15738 bool ValueNode::hasCompileTimeValue(size_t manIdx) const { return isNil || ASTNode::hasCompileTimeValue(manIdx); }
523
524 2522 CompileTimeValue ValueNode::getCompileTimeValue(size_t manIdx) const {
525
2/2
✓ Branch 2 → 3 taken 1110 times.
✓ Branch 2 → 4 taken 1412 times.
2522 return isNil ? CompileTimeValue{.longValue = 0} : ASTNode::getCompileTimeValue(manIdx);
526 }
527
528 30990 bool FctCallNode::hasCompileTimeValue(size_t manIdx) const {
529
6/8
✓ Branch 3 → 4 taken 30990 times.
✗ Branch 3 → 11 not taken.
✓ Branch 4 → 5 taken 16098 times.
✓ Branch 4 → 8 taken 14892 times.
✓ Branch 5 → 6 taken 16098 times.
✗ Branch 5 → 11 not taken.
✓ Branch 6 → 7 taken 3824 times.
✓ Branch 6 → 8 taken 12274 times.
30990 return BUILTIN_FUNCTIONS_MAP.contains(fqFunctionName) && data.at(manIdx).compileTimeValueSet;
530 }
531
532 4480 CompileTimeValue FctCallNode::getCompileTimeValue(size_t manIdx) const { return data.at(manIdx).compileTimeValue; }
533
534 770 void FctCallNode::setCompileTimeValue(const CompileTimeValue &value, size_t manIdx) {
535 770 data.at(manIdx).setCompileTimeValue(value);
536 770 }
537
538 42987 bool FctCallNode::returnsOnAllControlPaths(bool *overrideUnreachable, size_t manIdx) const {
539 // Some builtin functions terminate the control flow, e.g. panic
540
1/2
✓ Branch 3 → 4 taken 42987 times.
✗ Branch 3 → 13 not taken.
42987 const auto it = BUILTIN_FUNCTIONS_MAP.find(fqFunctionName);
541
4/4
✓ Branch 6 → 7 taken 7786 times.
✓ Branch 6 → 10 taken 35201 times.
✓ Branch 8 → 9 taken 5107 times.
✓ Branch 8 → 10 taken 2679 times.
42987 return it != BUILTIN_FUNCTIONS_MAP.end() && it->second.isFunctionTerminator;
542 }
543
544 /**
545 * Check if right above the closest assign expression ancestor is a statement node
546 *
547 * @return Has return value receiver or not
548 */
549 81866 bool FctCallNode::hasReturnValueReceiver() const {
550 81866 const ASTNode *node = parent;
551
2/2
✓ Branch 13 → 3 taken 291077 times.
✓ Branch 13 → 14 taken 6596 times.
297673 while (!node->isAssignExpr()) {
552
2/2
✓ Branch 4 → 5 taken 9178 times.
✓ Branch 4 → 6 taken 281899 times.
291077 if (node->isExprStmt())
553 9178 return false;
554 // As soon as we have a node with more than one child, we know that the return value is used
555
3/4
✓ Branch 6 → 7 taken 281899 times.
✗ Branch 6 → 25 not taken.
✓ Branch 9 → 10 taken 66092 times.
✓ Branch 9 → 11 taken 215807 times.
281899 if (node->getChildren().size() > 1)
556 66092 return true;
557 215807 node = node->parent;
558 }
559 // Also check the condition of the assign expression
560
3/12
✓ Branch 14 → 15 taken 6596 times.
✗ Branch 14 → 26 not taken.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 19 taken 6596 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 6596 times.
✗ Branch 21 → 23 not taken.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
6596 return node->getChildren().size() > 1 || !node->parent->isExprStmt();
561 }
562
563 109 bool LambdaFuncNode::returnsOnAllControlPaths(bool *overrideUnreachable, size_t manIdx) const {
564 109 return body->returnsOnAllControlPaths(overrideUnreachable, manIdx);
565 }
566
567 92 bool LambdaProcNode::returnsOnAllControlPaths(bool *overrideUnreachable, size_t manIdx) const {
568 92 return body->returnsOnAllControlPaths(overrideUnreachable, manIdx);
569 }
570
571 17411 void DataTypeNode::setFieldTypeRecursive() { // NOLINT(*-no-recursion)
572 // Set the current node to field type
573 17411 isFieldType = true;
574 // Do the same for all template nodes
575
4/4
✓ Branch 2 → 3 taken 9802 times.
✓ Branch 2 → 20 taken 7609 times.
✓ Branch 3 → 4 taken 1421 times.
✓ Branch 3 → 20 taken 8381 times.
17411 if (const CustomDataTypeNode *customType = baseDataType->customDataType; customType != nullptr && customType->templateTypeLst)
576
2/2
✓ Branch 18 → 6 taken 1952 times.
✓ Branch 18 → 19 taken 1421 times.
4794 for (DataTypeNode *templateNode : customType->templateTypeLst->dataTypes)
577
1/2
✓ Branch 8 → 9 taken 1952 times.
✗ Branch 8 → 21 not taken.
1952 templateNode->setFieldTypeRecursive();
578 17411 }
579
580 } // namespace spice::compiler
581