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 5683 std::string ASTNode::getErrorMessage() const {
19 5683 antlr4::CharStream *inputStream = codeLoc.sourceFile->antlrCtx.inputStream.get();
20 5683 const antlr4::misc::Interval &sourceInterval = codeLoc.sourceInterval;
21 5683 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 5683 times.
✗ Branch 3 → 77 not taken.
✓ Branch 6 → 7 taken 38 times.
✓ Branch 6 → 8 taken 5645 times.
5683 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 5683 size_t markerIndentation = 0;
28
2/2
✓ Branch 20 → 9 taken 68284 times.
✓ Branch 20 → 21 taken 1779 times.
70063 for (; markerIndentation < ERROR_MESSAGE_CONTEXT; markerIndentation++) {
29 68284 extSourceInterval.a--;
30
9/12
✓ Branch 9 → 10 taken 68252 times.
✓ Branch 9 → 13 taken 32 times.
✓ Branch 10 → 11 taken 68252 times.
✗ Branch 10 → 78 not taken.
✓ Branch 12 → 13 taken 3872 times.
✓ Branch 12 → 14 taken 64380 times.
✓ Branch 15 → 16 taken 68252 times.
✓ Branch 15 → 17 taken 32 times.
✓ Branch 17 → 18 taken 3904 times.
✓ Branch 17 → 19 taken 64380 times.
✗ Branch 78 → 79 not taken.
✗ Branch 78 → 80 not taken.
68284 if (extSourceInterval.a < 0 || inputStream->getText(extSourceInterval).find('\n') != std::string::npos) {
31 3904 extSourceInterval.a++;
32 3904 break;
33 }
34 }
35
2/2
✓ Branch 34 → 22 taken 17970 times.
✓ Branch 34 → 35 taken 120 times.
18090 for (size_t suffixContext = 0; suffixContext < ERROR_MESSAGE_CONTEXT; suffixContext++) {
36 17970 extSourceInterval.b++;
37
4/6
✓ Branch 22 → 23 taken 17970 times.
✗ Branch 22 → 82 not taken.
✓ Branch 23 → 24 taken 17970 times.
✗ Branch 23 → 27 not taken.
✓ Branch 26 → 27 taken 5563 times.
✓ Branch 26 → 28 taken 12407 times.
35940 if (static_cast<size_t>(extSourceInterval.b) > inputStream->size() ||
38
4/8
✓ Branch 24 → 25 taken 17970 times.
✗ Branch 24 → 82 not taken.
✓ Branch 29 → 30 taken 17970 times.
✗ Branch 29 → 31 not taken.
✓ Branch 31 → 32 taken 5563 times.
✓ Branch 31 → 33 taken 12407 times.
✗ Branch 82 → 83 not taken.
✗ Branch 82 → 84 not taken.
35940 inputStream->getText(extSourceInterval).find('\n') != std::string::npos) {
39 5563 extSourceInterval.b--;
40 5563 break;
41 }
42 }
43
44 // Trim start
45
3/4
✓ Branch 37 → 38 taken 33962 times.
✗ Branch 37 → 86 not taken.
✓ Branch 40 → 36 taken 28279 times.
✓ Branch 40 → 41 taken 5683 times.
33962 while (inputStream->getText(extSourceInterval)[0] == ' ') {
46 28279 extSourceInterval.a++;
47 28279 markerIndentation--;
48 }
49
50 // Trim end
51
3/4
✓ Branch 41 → 42 taken 5683 times.
✗ Branch 41 → 87 not taken.
✓ Branch 45 → 46 taken 38 times.
✓ Branch 45 → 47 taken 5645 times.
5683 if (inputStream->getText(extSourceInterval)[extSourceInterval.length() - 1] == '\n')
52 38 extSourceInterval.b--;
53
54 5683 const std::string lineNumberStr = std::to_string(codeLoc.line);
55 5683 markerIndentation += lineNumberStr.length() + 2;
56
57 // Build error message
58
1/2
✓ Branch 49 → 50 taken 5683 times.
✗ Branch 49 → 107 not taken.
5683 std::stringstream ss;
59
5/10
✓ Branch 50 → 51 taken 5683 times.
✗ Branch 50 → 105 not taken.
✓ Branch 51 → 52 taken 5683 times.
✗ Branch 51 → 105 not taken.
✓ Branch 52 → 53 taken 5683 times.
✗ Branch 52 → 90 not taken.
✓ Branch 53 → 54 taken 5683 times.
✗ Branch 53 → 88 not taken.
✓ Branch 54 → 55 taken 5683 times.
✗ Branch 54 → 88 not taken.
5683 ss << lineNumberStr << " " << inputStream->getText(extSourceInterval) << "\n";
60
2/4
✓ Branch 58 → 59 taken 5683 times.
✗ Branch 58 → 93 not taken.
✓ Branch 59 → 60 taken 5683 times.
✗ Branch 59 → 91 not taken.
11366 ss << std::string(markerIndentation, ' ');
61
2/4
✓ Branch 67 → 68 taken 5683 times.
✗ Branch 67 → 99 not taken.
✓ Branch 68 → 69 taken 5683 times.
✗ Branch 68 → 97 not taken.
5683 ss << std::string(std::min(sourceInterval.length(), extSourceInterval.length()), '^');
62
1/2
✓ Branch 71 → 72 taken 5683 times.
✗ Branch 71 → 105 not taken.
11366 return ss.str();
63 5683 }
64
65 271589 const StmtLstNode *ASTNode::getNextOuterStmtLst() const { // NOLINT(*-no-recursion)
66
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 271589 times.
271589 assert(parent != nullptr);
67
2/2
✓ Branch 5 → 6 taken 126974 times.
✓ Branch 5 → 16 taken 144615 times.
398563 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 1208 bool MainFctDefNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
76 1208 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
77 }
78
79 80548 bool FctDefBaseNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
80 80548 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
81 }
82
83 887 CompileTimeValue GlobalVarDefNode::getCompileTimeValue(size_t manIdx) const { return constant->getCompileTimeValue(manIdx); }
84
85 5811 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 5809 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 2 times.
5811 const bool condAlwaysTrue = condAssign->hasCompileTimeValue(manIdx) && condAssign->getCompileTimeValue(manIdx).boolValue;
89
1/4
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 12 taken 5811 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
5811 return condAlwaysTrue && body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
90 }
91
92 2997 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 2881 times.
✓ Branch 5 → 6 taken 116 times.
✗ Branch 5 → 7 not taken.
2997 const bool condAlwaysTrue = condition->hasCompileTimeValue(manIdx) && condition->getCompileTimeValue(manIdx).boolValue;
96
4/4
✓ Branch 8 → 9 taken 116 times.
✓ Branch 8 → 12 taken 2881 times.
✓ Branch 10 → 11 taken 18 times.
✓ Branch 10 → 12 taken 98 times.
2997 return condAlwaysTrue && body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
97 }
98
99 37 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 37 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
102 }
103
104 32836 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 32836 times.
32836 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 32836 times.
32836 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 23864 times.
✓ Branch 15 → 20 taken 8972 times.
✓ Branch 16 → 17 taken 301 times.
✓ Branch 16 → 20 taken 23563 times.
33137 return thenBody->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx) && elseStmt != nullptr &&
115
2/2
✓ Branch 18 → 19 taken 249 times.
✓ Branch 18 → 20 taken 52 times.
33137 elseStmt->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
116 }
117
118 301 bool ElseStmtNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable,
119 size_t manIdx) const { // NOLINT(misc-no-recursion)
120
2/2
✓ Branch 2 → 3 taken 110 times.
✓ Branch 2 → 5 taken 191 times.
301 if (isElseIf)
121 110 return ifStmt->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
122 191 return body->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx);
123 }
124
125 188 bool SwitchStmtNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
126 1208 const auto pred = [=](const CaseBranchNode *node) {
127 1020 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 1020 bool CaseBranchNode::returnsOnAllControlPaths(bool *doSetPredecessorsUnreachable, size_t manIdx) const {
136 1020 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 132400 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 439 times.
✓ Branch 3 → 5 taken 131961 times.
132400 if (statements.empty())
146 439 return false;
147 // A statement list returns on all control paths, if the one direct child statement returns on all control paths
148 131961 bool returnsOnAllControlPaths = false;
149
2/2
✓ Branch 26 → 7 taken 259356 times.
✓ Branch 26 → 27 taken 131961 times.
523278 for (StmtNode *child : statements) {
150
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 259356 times.
259356 assert(child != nullptr);
151
152 // Prevent marking instructions as unreachable if doSetPredecessorsUnreachable is set to false
153
4/4
✓ Branch 11 → 12 taken 4812 times.
✓ Branch 11 → 14 taken 254544 times.
✓ Branch 12 → 13 taken 6 times.
✓ Branch 12 → 14 taken 4806 times.
259356 if (returnsOnAllControlPaths && *doSetPredecessorsUnreachable)
154 6 child->unreachable = true;
155
156
3/4
✓ Branch 14 → 15 taken 259356 times.
✗ Branch 14 → 29 not taken.
✓ Branch 15 → 16 taken 81635 times.
✓ Branch 15 → 17 taken 177721 times.
259356 if (child->returnsOnAllControlPaths(doSetPredecessorsUnreachable, manIdx))
157 81635 returnsOnAllControlPaths = true;
158 }
159 131961 return returnsOnAllControlPaths;
160 }
161
162 14872 std::vector<const CompileTimeValue *> AttrLstNode::getAttrValuesByName(const std::string &key) const {
163
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 14872 times.
14872 assert(ATTR_CONFIGS.contains(key));
164
165 14872 std::vector<const CompileTimeValue *> attributeValues;
166
2/2
✓ Branch 28 → 7 taken 25528 times.
✓ Branch 28 → 29 taken 14872 times.
55272 for (const AttrNode *attrNode : attributes) {
167 // Skip attributes with different keys
168
2/2
✓ Branch 10 → 11 taken 19160 times.
✓ Branch 10 → 12 taken 6368 times.
25528 if (attrNode->key != key)
169 19160 continue;
170
171 // Found a matching attribute
172 6368 const CompileTimeValue *value = attrNode->getValue();
173
2/2
✓ Branch 13 → 14 taken 514 times.
✓ Branch 13 → 16 taken 5854 times.
6368 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 5854 times.
✗ Branch 16 → 32 not taken.
5854 attributeValues.push_back(value);
179 }
180 }
181
182 14872 return attributeValues;
183 }
184
185 8134 const CompileTimeValue *AttrLstNode::getAttrValueByName(const std::string &key) const {
186
1/2
✓ Branch 2 → 3 taken 8134 times.
✗ Branch 2 → 12 not taken.
8134 const std::vector<const CompileTimeValue *> attrs = getAttrValuesByName(key);
187
2/2
✓ Branch 4 → 5 taken 4792 times.
✓ Branch 4 → 6 taken 3342 times.
16268 return attrs.empty() ? nullptr : attrs.back();
188 8134 }
189
190 12634 bool AttrLstNode::hasAttr(const std::string &key) const {
191 38394 return std::ranges::any_of(attributes, [&](const AttrNode *attr) { return attr->key == key; });
192 }
193
194
2/2
✓ Branch 2 → 3 taken 5854 times.
✓ Branch 2 → 4 taken 514 times.
6368 const CompileTimeValue *AttrNode::getValue() const { return value ? &value->compileTimeValue : nullptr; }
195
196 42452 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 42452 times.
42452 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 42452 const AtomicExprNode *atomicExpr = getLhsAtomicNode();
203
6/6
✓ Branch 6 → 7 taken 41965 times.
✓ Branch 6 → 10 taken 487 times.
✓ Branch 8 → 9 taken 4086 times.
✓ Branch 8 → 10 taken 37879 times.
✓ Branch 11 → 12 taken 4086 times.
✓ Branch 11 → 13 taken 38366 times.
42452 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 4086 *doSetPredecessorsUnreachable = false;
207 4086 return true;
208 }
209
210 38366 return false;
211 }
212
213 42452 AtomicExprNode *AssignExprNode::getLhsAtomicNode() const {
214
3/4
✓ Branch 2 → 3 taken 42452 times.
✗ Branch 2 → 4 not taken.
✓ Branch 5 → 6 taken 8773 times.
✓ Branch 5 → 7 taken 33679 times.
42452 if (auto *atomicNode = dynamic_cast<AtomicExprNode *>(lhs))
215 8773 return atomicNode;
216
4/6
✓ Branch 7 → 8 taken 33679 times.
✗ Branch 7 → 28 not taken.
✓ Branch 9 → 10 taken 33679 times.
✗ Branch 9 → 11 not taken.
✓ Branch 13 → 14 taken 31655 times.
✓ Branch 13 → 15 taken 2024 times.
33679 if (auto *atomicNode = dynamic_cast<AtomicExprNode *>(lhs->getChildren().back()))
217 31655 return atomicNode;
218
5/8
✓ Branch 15 → 16 taken 2024 times.
✗ Branch 15 → 31 not taken.
✓ Branch 17 → 18 taken 2024 times.
✗ Branch 17 → 29 not taken.
✓ Branch 19 → 20 taken 2024 times.
✗ Branch 19 → 21 not taken.
✓ Branch 24 → 25 taken 1537 times.
✓ Branch 24 → 26 taken 487 times.
2024 if (auto *atomicNode = dynamic_cast<AtomicExprNode *>(lhs->getChildren().back()->getChildren().front()))
219 1537 return atomicNode;
220 487 return nullptr;
221 }
222
223 18 bool TernaryExprNode::hasCompileTimeValue(size_t manIdx) const {
224
3/4
✓ Branch 2 → 3 taken 18 times.
✗ Branch 2 → 5 not taken.
✓ Branch 4 → 5 taken 10 times.
✓ Branch 4 → 6 taken 8 times.
18 const bool trueExprHasCompileTimeValue = !trueExpr || trueExpr->hasCompileTimeValue(manIdx);
225
3/4
✓ Branch 7 → 8 taken 18 times.
✗ Branch 7 → 10 not taken.
✓ Branch 9 → 10 taken 12 times.
✓ Branch 9 → 11 taken 6 times.
18 const bool falseExprHasCompileTimeValue = !falseExpr || falseExpr->hasCompileTimeValue(manIdx);
226
1/6
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 17 taken 18 times.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 17 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
18 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 2350 bool LogicalOrExprNode::hasCompileTimeValue(size_t manIdx) const {
250 4700 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 2440 bool LogicalAndExprNode::hasCompileTimeValue(size_t manIdx) const {
270 4880 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 506 bool BitwiseOrExprNode::hasCompileTimeValue(size_t manIdx) const {
290 1012 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 32740 bool EqualityExprNode::hasCompileTimeValue(size_t manIdx) const {
344 65516 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 20499 bool RelationalExprNode::hasCompileTimeValue(size_t manIdx) const {
362 41062 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 1152 bool AdditiveExprNode::hasCompileTimeValue(size_t manIdx) const {
409 2308 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 1078 bool MultiplicativeExprNode::hasCompileTimeValue(size_t manIdx) const {
434 2158 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 1352 bool CastExprNode::hasCompileTimeValue(size_t manIdx) const {
464
1/2
✓ Branch 2 → 3 taken 1352 times.
✗ Branch 2 → 5 not taken.
1352 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 6794 bool PrefixUnaryExprNode::hasCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
472
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 6794 times.
6794 if (postfixUnaryExpr)
473 return postfixUnaryExpr->hasCompileTimeValue(manIdx);
474
475
3/4
✓ Branch 6 → 7 taken 6712 times.
✓ Branch 6 → 11 taken 82 times.
✓ Branch 7 → 8 taken 6712 times.
✗ Branch 7 → 11 not taken.
6794 const bool isSupported = op == PrefixUnaryOp::OP_NONE || op == PrefixUnaryOp::OP_MINUS || op == PrefixUnaryOp::OP_PLUS_PLUS ||
476
4/6
✓ Branch 5 → 6 taken 6794 times.
✗ Branch 5 → 11 not taken.
✓ Branch 8 → 9 taken 6712 times.
✗ Branch 8 → 11 not taken.
✓ Branch 9 → 10 taken 183 times.
✓ Branch 9 → 11 taken 6529 times.
13771 op == PrefixUnaryOp::OP_MINUS_MINUS || op == PrefixUnaryOp::OP_NOT ||
477
2/2
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 12 taken 181 times.
183 op == PrefixUnaryOp::OP_BITWISE_NOT;
478
4/4
✓ Branch 13 → 14 taken 6613 times.
✓ Branch 13 → 17 taken 181 times.
✓ Branch 15 → 16 taken 650 times.
✓ Branch 15 → 17 taken 5963 times.
6794 return isSupported && prefixUnaryExpr->hasCompileTimeValue(manIdx);
479 }
480
481 1300 CompileTimeValue PrefixUnaryExprNode::getCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
482
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 1300 times.
1300 if (postfixUnaryExpr)
483 return postfixUnaryExpr->getCompileTimeValue(manIdx);
484
485
1/2
✓ Branch 5 → 6 taken 1300 times.
✗ Branch 5 → 35 not taken.
1300 CompileTimeValue opValue = prefixUnaryExpr->getCompileTimeValue(manIdx);
486
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 1300 times.
1300 if (op == PrefixUnaryOp::OP_MINUS)
487 return CompileTimeValue{.longValue = -opValue.longValue};
488
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 1300 times.
1300 if (op == PrefixUnaryOp::OP_PLUS_PLUS)
489 return CompileTimeValue{.longValue = ++opValue.longValue};
490
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 1300 times.
1300 if (op == PrefixUnaryOp::OP_MINUS_MINUS)
491 return CompileTimeValue{.longValue = --opValue.longValue};
492
1/2
✓ Branch 12 → 13 taken 1300 times.
✗ Branch 12 → 14 not taken.
1300 if (op == PrefixUnaryOp::OP_NOT)
493 1300 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 19558 bool PostfixUnaryExprNode::hasCompileTimeValue(size_t manIdx) const { // NOLINT(*-no-recursion)
501
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 19558 times.
19558 if (atomicExpr)
502 return atomicExpr->hasCompileTimeValue(manIdx);
503
504 19558 const bool isSupported =
505
4/6
✓ Branch 5 → 6 taken 19558 times.
✗ Branch 5 → 8 not taken.
✓ Branch 6 → 7 taken 19558 times.
✗ Branch 6 → 8 not taken.
✓ Branch 7 → 8 taken 6 times.
✓ Branch 7 → 9 taken 19552 times.
19558 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 19552 times.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 6 times.
19558 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 16156 times.
✓ Branch 2 → 5 taken 20 times.
✓ Branch 4 → 5 taken 768 times.
✓ Branch 4 → 6 taken 15388 times.
16176 bool ValueNode::hasCompileTimeValue(size_t manIdx) const { return isNil || ASTNode::hasCompileTimeValue(manIdx); }
523
524 2568 CompileTimeValue ValueNode::getCompileTimeValue(size_t manIdx) const {
525
2/2
✓ Branch 2 → 3 taken 1132 times.
✓ Branch 2 → 4 taken 1436 times.
2568 return isNil ? CompileTimeValue{.longValue = 0} : ASTNode::getCompileTimeValue(manIdx);
526 }
527
528 31768 bool FctCallNode::hasCompileTimeValue(size_t manIdx) const {
529
6/8
✓ Branch 3 → 4 taken 31768 times.
✗ Branch 3 → 11 not taken.
✓ Branch 4 → 5 taken 16450 times.
✓ Branch 4 → 8 taken 15318 times.
✓ Branch 5 → 6 taken 16450 times.
✗ Branch 5 → 11 not taken.
✓ Branch 6 → 7 taken 3868 times.
✓ Branch 6 → 8 taken 12582 times.
31768 return BUILTIN_FUNCTIONS_MAP.contains(fqFunctionName) && data.at(manIdx).compileTimeValueSet;
530 }
531
532 4536 CompileTimeValue FctCallNode::getCompileTimeValue(size_t manIdx) const { return data.at(manIdx).compileTimeValue; }
533
534 782 void FctCallNode::setCompileTimeValue(const CompileTimeValue &value, size_t manIdx) {
535 782 data.at(manIdx).setCompileTimeValue(value);
536 782 }
537
538 44493 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 44493 times.
✗ Branch 3 → 13 not taken.
44493 const auto it = BUILTIN_FUNCTIONS_MAP.find(fqFunctionName);
541
4/4
✓ Branch 6 → 7 taken 8052 times.
✓ Branch 6 → 10 taken 36441 times.
✓ Branch 8 → 9 taken 5323 times.
✓ Branch 8 → 10 taken 2729 times.
44493 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 84548 bool FctCallNode::hasReturnValueReceiver() const {
550 84548 const ASTNode *node = parent;
551
2/2
✓ Branch 13 → 3 taken 299867 times.
✓ Branch 13 → 14 taken 6898 times.
306765 while (!node->isAssignExpr()) {
552
2/2
✓ Branch 4 → 5 taken 9550 times.
✓ Branch 4 → 6 taken 290317 times.
299867 if (node->isExprStmt())
553 9550 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 290317 times.
✗ Branch 6 → 25 not taken.
✓ Branch 9 → 10 taken 68100 times.
✓ Branch 9 → 11 taken 222217 times.
290317 if (node->getChildren().size() > 1)
556 68100 return true;
557 222217 node = node->parent;
558 }
559 // Also check the condition of the assign expression
560
3/12
✓ Branch 14 → 15 taken 6898 times.
✗ Branch 14 → 26 not taken.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 19 taken 6898 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 6898 times.
✗ Branch 21 → 23 not taken.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
6898 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 17891 void DataTypeNode::setFieldTypeRecursive() { // NOLINT(*-no-recursion)
572 // Set the current node to field type
573 17891 isFieldType = true;
574 // Do the same for all template nodes
575
4/4
✓ Branch 2 → 3 taken 9956 times.
✓ Branch 2 → 20 taken 7935 times.
✓ Branch 3 → 4 taken 1439 times.
✓ Branch 3 → 20 taken 8517 times.
17891 if (const CustomDataTypeNode *customType = baseDataType->customDataType; customType != nullptr && customType->templateTypeLst)
576
2/2
✓ Branch 18 → 6 taken 1970 times.
✓ Branch 18 → 19 taken 1439 times.
4848 for (DataTypeNode *templateNode : customType->templateTypeLst->dataTypes)
577
1/2
✓ Branch 8 → 9 taken 1970 times.
✗ Branch 8 → 21 not taken.
1970 templateNode->setFieldTypeRecursive();
578 17891 }
579
580 } // namespace spice::compiler
581