GCC Code Coverage Report


Directory: ../
File: src/typechecker/TypeCheckerStatements.cpp
Date: 2025-10-27 22:48:14
Coverage Exec Excl Total
Lines: 94.8% 91 0 96
Functions: 100.0% 7 0 7
Branches: 61.0% 183 0 300

Line Branch Exec Source
1 // Copyright (c) 2021-2025 ChilliBits. All rights reserved.
2
3 #include "TypeChecker.h"
4
5 #include <SourceFile.h>
6 #include <ast/ASTNodes.h>
7 #include <global/GlobalResourceManager.h>
8 #include <symboltablebuilder/SymbolTableBuilder.h>
9 #include <typechecker/MacroDefs.h>
10
11 namespace spice::compiler {
12
13 19383 std::any TypeChecker::visitStmtLst(StmtLstNode *node) {
14 // Visit nodes in this scope
15
2/2
✓ Branch 15 → 4 taken 36846 times.
✓ Branch 15 → 16 taken 19352 times.
56198 for (StmtNode *stmt : node->statements) {
16
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 36846 times.
36846 if (!stmt)
17 continue;
18 // Print warning if statement is unreachable
19
2/2
✓ Branch 7 → 8 taken 5 times.
✓ Branch 7 → 10 taken 36841 times.
36846 if (stmt->unreachable) {
20
1/2
✓ Branch 8 → 9 taken 5 times.
✗ Branch 8 → 20 not taken.
5 warnings.emplace_back(stmt->codeLoc, UNREACHABLE_CODE, "This statement is unreachable");
21 5 continue;
22 }
23 // Visit the statement
24
2/2
✓ Branch 10 → 11 taken 36810 times.
✓ Branch 10 → 21 taken 31 times.
36841 visit(stmt);
25 }
26
27 // Do cleanup of this scope, e.g. dtor calls for struct instances
28 19352 doScopeCleanup(node);
29
30
1/2
✓ Branch 17 → 18 taken 19352 times.
✗ Branch 17 → 23 not taken.
19352 return nullptr;
31 }
32
33 32176 std::any TypeChecker::visitDeclStmt(DeclStmtNode *node) {
34 // Retrieve entry of the lhs variable
35
1/2
✓ Branch 2 → 3 taken 32176 times.
✗ Branch 2 → 179 not taken.
32176 SymbolTableEntry *localVarEntry = currentScope->lookupStrict(node->varName);
36
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 32176 times.
32176 assert(localVarEntry != nullptr);
37
38 32176 QualType localVarType;
39
2/2
✓ Branch 7 → 8 taken 9471 times.
✓ Branch 7 → 56 taken 22705 times.
32176 if (node->hasAssignment) {
40 // Visit the right side
41
3/4
✓ Branch 8 → 9 taken 9466 times.
✓ Branch 8 → 129 taken 5 times.
✓ Branch 9 → 10 taken 9466 times.
✗ Branch 9 → 127 not taken.
9471 auto rhs = std::any_cast<ExprResult>(visit(node->assignExpr));
42 9466 auto [rhsTy, rhsEntry] = rhs;
43
44 // Visit data type
45
3/4
✓ Branch 11 → 12 taken 9463 times.
✓ Branch 11 → 132 taken 3 times.
✓ Branch 12 → 13 taken 9463 times.
✗ Branch 12 → 130 not taken.
9466 localVarType = std::any_cast<QualType>(visit(node->dataType));
46
47 // Check if type has to be inferred or both types are fixed
48
8/10
✓ Branch 14 → 15 taken 9463 times.
✗ Branch 14 → 148 not taken.
✓ Branch 15 → 16 taken 9459 times.
✓ Branch 15 → 19 taken 4 times.
✓ Branch 16 → 17 taken 9459 times.
✗ Branch 16 → 148 not taken.
✓ Branch 17 → 18 taken 9438 times.
✓ Branch 17 → 19 taken 21 times.
✓ Branch 20 → 21 taken 9438 times.
✓ Branch 20 → 45 taken 25 times.
9463 if (!localVarType.is(TY_UNRESOLVED) && !rhsTy.is(TY_UNRESOLVED)) {
49 9438 const ExprResult lhsResult = {localVarType, localVarEntry};
50
2/2
✓ Branch 21 → 22 taken 9428 times.
✓ Branch 21 → 146 taken 10 times.
9438 const auto [type, copyCtor] = opRuleManager.getAssignResultType(node, lhsResult, rhs, true);
51 9428 localVarType = type;
52 9428 node->calledCopyCtor = copyCtor;
53
54 // If this is a struct type, check if the type is known. If not, error out
55
8/14
✓ Branch 24 → 25 taken 9428 times.
✗ Branch 24 → 134 not taken.
✓ Branch 25 → 26 taken 1200 times.
✓ Branch 25 → 31 taken 8228 times.
✓ Branch 26 → 27 taken 1200 times.
✗ Branch 26 → 134 not taken.
✓ Branch 27 → 28 taken 1200 times.
✗ Branch 27 → 134 not taken.
✓ Branch 28 → 29 taken 1200 times.
✗ Branch 28 → 134 not taken.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 1200 times.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 44 taken 9428 times.
9428 if (localVarType.isBase(TY_STRUCT) && !sourceFile->getNameRegistryEntry(localVarType.getBase().getSubType())) {
56 const std::string structName = localVarType.getBase().getSubType();
57 softError(node->dataType, UNKNOWN_DATATYPE, "Unknown struct type '" + structName + "'. Forgot to import?");
58 localVarType = QualType(TY_UNRESOLVED);
59 }
60 } else {
61
1/2
✓ Branch 45 → 46 taken 25 times.
✗ Branch 45 → 147 not taken.
25 localVarType = QualType(TY_UNRESOLVED);
62 }
63
64 // If there is an anonymous entry attached (e.g. for struct instantiation) and we take over ownership, delete it
65
9/10
✓ Branch 47 → 48 taken 9453 times.
✗ Branch 47 → 148 not taken.
✓ Branch 48 → 49 taken 9285 times.
✓ Branch 48 → 52 taken 168 times.
✓ Branch 49 → 50 taken 1801 times.
✓ Branch 49 → 52 taken 7484 times.
✓ Branch 50 → 51 taken 786 times.
✓ Branch 50 → 52 taken 1015 times.
✓ Branch 53 → 54 taken 786 times.
✓ Branch 53 → 55 taken 8667 times.
9453 if (!localVarType.isRef() && rhsEntry != nullptr && rhsEntry->anonymous)
66
1/2
✓ Branch 54 → 55 taken 786 times.
✗ Branch 54 → 148 not taken.
786 currentScope->symbolTable.deleteAnonymous(rhsEntry->name);
67 } else {
68 // Visit data type
69
2/4
✓ Branch 56 → 57 taken 22705 times.
✗ Branch 56 → 151 not taken.
✓ Branch 57 → 58 taken 22705 times.
✗ Branch 57 → 149 not taken.
22705 localVarType = std::any_cast<QualType>(visit(node->dataType));
70
71 // References with no initialization are illegal
72
9/10
✓ Branch 59 → 60 taken 22705 times.
✗ Branch 59 → 179 not taken.
✓ Branch 60 → 61 taken 6517 times.
✓ Branch 60 → 64 taken 16188 times.
✓ Branch 61 → 62 taken 80 times.
✓ Branch 61 → 64 taken 6437 times.
✓ Branch 62 → 63 taken 1 time.
✓ Branch 62 → 64 taken 79 times.
✓ Branch 65 → 66 taken 1 time.
✓ Branch 65 → 73 taken 22704 times.
22705 if (localVarType.isRef() && !node->isFctParam && !node->isForEachItem)
73
2/4
✓ Branch 68 → 69 taken 1 time.
✗ Branch 68 → 155 not taken.
✓ Branch 69 → 70 taken 1 time.
✗ Branch 69 → 153 not taken.
2 softError(node, REFERENCE_WITHOUT_INITIALIZER, "References must always be initialized directly");
74
75 // If this is a struct, check for the default ctor
76
9/10
✓ Branch 73 → 74 taken 22705 times.
✗ Branch 73 → 179 not taken.
✓ Branch 74 → 75 taken 707 times.
✓ Branch 74 → 78 taken 21998 times.
✓ Branch 75 → 76 taken 174 times.
✓ Branch 75 → 78 taken 533 times.
✓ Branch 76 → 77 taken 165 times.
✓ Branch 76 → 78 taken 9 times.
✓ Branch 79 → 80 taken 165 times.
✓ Branch 79 → 120 taken 22540 times.
22705 if (localVarType.is(TY_STRUCT) && !node->isFctParam && !node->isForEachItem) {
77
1/2
✓ Branch 80 → 81 taken 165 times.
✗ Branch 80 → 179 not taken.
165 Scope *matchScope = localVarType.getBodyScope();
78
1/2
✗ Branch 81 → 82 not taken.
✓ Branch 81 → 83 taken 165 times.
165 assert(matchScope != nullptr);
79 // Check if we are required to call a ctor
80
1/2
✓ Branch 83 → 84 taken 165 times.
✗ Branch 83 → 179 not taken.
165 const Struct *spiceStruct = localVarType.getStruct(node);
81
1/2
✗ Branch 84 → 85 not taken.
✓ Branch 84 → 86 taken 165 times.
165 assert(spiceStruct != nullptr);
82
1/2
✓ Branch 86 → 87 taken 165 times.
✗ Branch 86 → 88 not taken.
165 auto structDeclNode = spice_pointer_cast<StructDefNode *>(spiceStruct->declNode);
83
5/6
✓ Branch 93 → 94 taken 165 times.
✗ Branch 93 → 179 not taken.
✓ Branch 94 → 95 taken 164 times.
✓ Branch 94 → 96 taken 1 time.
✓ Branch 95 → 96 taken 24 times.
✓ Branch 95 → 97 taken 140 times.
165 node->isCtorCallRequired = matchScope->hasRefFields() || structDeclNode->emitVTable;
84 // Check if we have a no-args ctor to call
85
1/2
✓ Branch 98 → 99 taken 165 times.
✗ Branch 98 → 179 not taken.
165 const std::string &structName = localVarType.getSubType();
86 165 const QualType &thisType = localVarType;
87
2/4
✓ Branch 103 → 104 taken 165 times.
✗ Branch 103 → 161 not taken.
✓ Branch 104 → 105 taken 165 times.
✗ Branch 104 → 159 not taken.
495 node->calledInitCtor = FunctionManager::match(matchScope, CTOR_FUNCTION_NAME, thisType, {}, {}, false, node);
88
4/4
✓ Branch 109 → 110 taken 15 times.
✓ Branch 109 → 120 taken 150 times.
✓ Branch 110 → 111 taken 2 times.
✓ Branch 110 → 120 taken 13 times.
165 if (!node->calledInitCtor && node->isCtorCallRequired)
89
5/10
✓ Branch 111 → 112 taken 2 times.
✗ Branch 111 → 175 not taken.
✓ Branch 112 → 113 taken 2 times.
✗ Branch 112 → 173 not taken.
✓ Branch 113 → 114 taken 2 times.
✗ Branch 113 → 171 not taken.
✓ Branch 116 → 117 taken 2 times.
✗ Branch 116 → 177 not taken.
✓ Branch 117 → 118 taken 2 times.
✗ Branch 117 → 177 not taken.
2 SOFT_ERROR_QT(node, MISSING_NO_ARGS_CTOR, "Struct '" + structName + "' misses a no-args constructor")
90 }
91 }
92
93 // Update the type of the variable
94
1/2
✓ Branch 120 → 121 taken 32156 times.
✗ Branch 120 → 179 not taken.
32156 localVarEntry->updateType(localVarType, true);
95
1/2
✓ Branch 121 → 122 taken 32156 times.
✗ Branch 121 → 179 not taken.
32156 node->entries.at(manIdx) = localVarEntry;
96
97 // Update the state of the variable
98
1/2
✓ Branch 122 → 123 taken 32156 times.
✗ Branch 122 → 178 not taken.
32156 localVarEntry->updateState(INITIALIZED, node);
99
100
1/2
✓ Branch 123 → 124 taken 32156 times.
✗ Branch 123 → 179 not taken.
32156 return localVarType;
101 }
102
103 9393 std::any TypeChecker::visitReturnStmt(ReturnStmtNode *node) {
104 // Retrieve return variable entry
105
1/2
✓ Branch 4 → 5 taken 9393 times.
✗ Branch 4 → 68 not taken.
28179 SymbolTableEntry *returnVar = currentScope->lookup(RETURN_VARIABLE_NAME);
106 9393 const bool isFunction = returnVar != nullptr;
107
4/6
✓ Branch 10 → 11 taken 9329 times.
✓ Branch 10 → 13 taken 64 times.
✓ Branch 11 → 12 taken 9329 times.
✗ Branch 11 → 91 not taken.
✓ Branch 13 → 14 taken 64 times.
✗ Branch 13 → 91 not taken.
9393 const QualType returnType = isFunction ? returnVar->getQualType() : QualType(TY_DYN);
108
109 // Check if procedure with return value
110
2/2
✓ Branch 14 → 15 taken 64 times.
✓ Branch 14 → 28 taken 9329 times.
9393 if (!isFunction) {
111
2/2
✓ Branch 15 → 16 taken 2 times.
✓ Branch 15 → 26 taken 62 times.
64 if (node->hasReturnValue)
112
4/8
✓ Branch 18 → 19 taken 2 times.
✗ Branch 18 → 74 not taken.
✓ Branch 19 → 20 taken 2 times.
✗ Branch 19 → 72 not taken.
✓ Branch 22 → 23 taken 2 times.
✗ Branch 22 → 78 not taken.
✓ Branch 23 → 24 taken 2 times.
✗ Branch 23 → 78 not taken.
6 SOFT_ERROR_ER(node->assignExpr, RETURN_WITH_VALUE_IN_PROCEDURE, "Return with value in procedure is not allowed")
113
1/2
✓ Branch 26 → 27 taken 62 times.
✗ Branch 26 → 79 not taken.
62 return nullptr;
114 }
115
116
7/8
✓ Branch 28 → 29 taken 6 times.
✓ Branch 28 → 33 taken 9323 times.
✓ Branch 30 → 31 taken 6 times.
✗ Branch 30 → 91 not taken.
✓ Branch 31 → 32 taken 2 times.
✓ Branch 31 → 33 taken 4 times.
✓ Branch 34 → 35 taken 2 times.
✓ Branch 34 → 45 taken 9327 times.
9329 if (!node->hasReturnValue && !returnVar->getLifecycle().isInitialized())
117
4/8
✓ Branch 37 → 38 taken 2 times.
✗ Branch 37 → 82 not taken.
✓ Branch 38 → 39 taken 2 times.
✗ Branch 38 → 80 not taken.
✓ Branch 41 → 42 taken 2 times.
✗ Branch 41 → 86 not taken.
✓ Branch 42 → 43 taken 2 times.
✗ Branch 42 → 86 not taken.
6 SOFT_ERROR_QT(node, RETURN_WITHOUT_VALUE_RESULT, "Return without value, but result variable is not initialized yet")
118
119
2/2
✓ Branch 45 → 46 taken 4 times.
✓ Branch 45 → 48 taken 9323 times.
9327 if (!node->hasReturnValue)
120
1/2
✓ Branch 46 → 47 taken 4 times.
✗ Branch 46 → 87 not taken.
4 return nullptr;
121
122 // Visit right side
123
2/4
✓ Branch 48 → 49 taken 9323 times.
✗ Branch 48 → 90 not taken.
✓ Branch 49 → 50 taken 9323 times.
✗ Branch 49 → 88 not taken.
9323 const auto rhs = std::any_cast<ExprResult>(visit(node->assignExpr));
124
2/6
✓ Branch 51 → 52 taken 9323 times.
✗ Branch 51 → 91 not taken.
✗ Branch 52 → 53 not taken.
✓ Branch 52 → 55 taken 9323 times.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 91 not taken.
9323 HANDLE_UNRESOLVED_TYPE_QT(rhs.type)
125
126 // Check if types match
127 9323 const ExprResult returnResult = {returnType, returnVar};
128
2/2
✓ Branch 55 → 56 taken 9320 times.
✓ Branch 55 → 91 taken 3 times.
9323 auto [_, copyCtor] = opRuleManager.getAssignResultType(node->assignExpr, returnResult, rhs, false, true, ERROR_MSG_RETURN);
129 9320 node->calledCopyCtor = copyCtor;
130
131 // Check if the dtor call on the return value can be skipped
132
2/2
✓ Branch 58 → 59 taken 2290 times.
✓ Branch 58 → 62 taken 7030 times.
9320 if (rhs.entry != nullptr) {
133
2/2
✓ Branch 59 → 60 taken 807 times.
✓ Branch 59 → 61 taken 1483 times.
2290 if (rhs.entry->anonymous) {
134 // If there is an anonymous entry attached (e.g. for struct instantiation), delete it
135
1/2
✓ Branch 60 → 62 taken 807 times.
✗ Branch 60 → 91 not taken.
807 currentScope->symbolTable.deleteAnonymous(rhs.entry->name);
136 } else {
137 // Otherwise omit the destructor call, because the caller destructs the value
138 1483 rhs.entry->omitDtorCall = true;
139 }
140 }
141
142
1/2
✓ Branch 62 → 63 taken 9320 times.
✗ Branch 62 → 91 not taken.
9320 return node->returnType = returnType;
143 }
144
145 114 std::any TypeChecker::visitBreakStmt(BreakStmtNode *node) {
146 // Check if the stated number is valid
147
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 12 taken 113 times.
114 if (node->breakTimes < 1)
148
4/8
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 30 not taken.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 28 not taken.
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 34 not taken.
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 34 not taken.
1 SOFT_ERROR_ER(node, INVALID_BREAK_NUMBER, "Break count must be >= 1, you provided " + std::to_string(node->breakTimes))
149
150 // Check if we can break this often
151 113 const unsigned int maxBreaks = currentScope->getLoopNestingDepth();
152
2/2
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 25 taken 112 times.
113 if (static_cast<unsigned int>(node->breakTimes) > maxBreaks)
153
5/10
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 39 not taken.
✓ Branch 16 → 17 taken 1 time.
✗ Branch 16 → 37 not taken.
✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 35 not taken.
✓ Branch 21 → 22 taken 1 time.
✗ Branch 21 → 44 not taken.
✓ Branch 22 → 23 taken 1 time.
✗ Branch 22 → 44 not taken.
1 SOFT_ERROR_ER(node, INVALID_BREAK_NUMBER, "We can only break " + std::to_string(maxBreaks) + " time(s) here")
154
155
1/2
✓ Branch 25 → 26 taken 112 times.
✗ Branch 25 → 45 not taken.
112 return nullptr;
156 }
157
158 375 std::any TypeChecker::visitContinueStmt(ContinueStmtNode *node) {
159 // Check if the stated number is valid
160
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 12 taken 374 times.
375 if (node->continueTimes < 1)
161
4/8
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 30 not taken.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 28 not taken.
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 34 not taken.
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 34 not taken.
1 SOFT_ERROR_ER(node, INVALID_CONTINUE_NUMBER,
162 "Continue count must be >= 1, you provided " + std::to_string(node->continueTimes))
163
164 // Check if we can continue this often
165 374 const unsigned int maxContinues = currentScope->getLoopNestingDepth();
166
2/2
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 25 taken 373 times.
374 if (static_cast<unsigned int>(node->continueTimes) > maxContinues)
167
5/10
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 39 not taken.
✓ Branch 16 → 17 taken 1 time.
✗ Branch 16 → 37 not taken.
✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 35 not taken.
✓ Branch 21 → 22 taken 1 time.
✗ Branch 21 → 44 not taken.
✓ Branch 22 → 23 taken 1 time.
✗ Branch 22 → 44 not taken.
1 SOFT_ERROR_ER(node, INVALID_CONTINUE_NUMBER, "We can only continue " + std::to_string(maxContinues) + " time(s) here")
168
169
1/2
✓ Branch 25 → 26 taken 373 times.
✗ Branch 25 → 45 not taken.
373 return nullptr;
170 }
171
172 6 std::any TypeChecker::visitFallthroughStmt(FallthroughStmtNode *node) {
173 // Check if we can do a fallthrough here
174
2/2
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 14 taken 4 times.
6 if (!currentScope->isInCaseBranch())
175
4/8
✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 19 not taken.
✓ Branch 7 → 8 taken 2 times.
✗ Branch 7 → 17 not taken.
✓ Branch 10 → 11 taken 2 times.
✗ Branch 10 → 23 not taken.
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 23 not taken.
6 SOFT_ERROR_ER(node, FALLTHROUGH_NOT_ALLOWED, "Fallthrough is only allowed in case branches")
176
177
1/2
✓ Branch 14 → 15 taken 4 times.
✗ Branch 14 → 24 not taken.
4 return nullptr;
178 }
179
180 764 std::any TypeChecker::visitAssertStmt(AssertStmtNode *node) {
181 // Visit condition
182
2/4
✓ Branch 2 → 3 taken 764 times.
✗ Branch 2 → 29 not taken.
✓ Branch 3 → 4 taken 764 times.
✗ Branch 3 → 27 not taken.
764 const QualType conditionType = std::any_cast<ExprResult>(visit(node->assignExpr)).type;
183
2/8
✓ Branch 5 → 6 taken 764 times.
✗ Branch 5 → 40 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 11 taken 764 times.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 31 not taken.
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 31 not taken.
764 HANDLE_UNRESOLVED_TYPE_ER(conditionType)
184
185 // Check if condition evaluates to bool
186
3/4
✓ Branch 11 → 12 taken 764 times.
✗ Branch 11 → 40 not taken.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 23 taken 763 times.
764 if (!conditionType.is(TY_BOOL))
187
4/8
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 34 not taken.
✓ Branch 16 → 17 taken 1 time.
✗ Branch 16 → 32 not taken.
✓ Branch 19 → 20 taken 1 time.
✗ Branch 19 → 38 not taken.
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 38 not taken.
3 SOFT_ERROR_ER(node->assignExpr, ASSERTION_CONDITION_BOOL, "The asserted condition must be of type bool")
188
189
1/2
✓ Branch 23 → 24 taken 763 times.
✗ Branch 23 → 39 not taken.
763 return nullptr;
190 }
191
192 } // namespace spice::compiler
193