GCC Code Coverage Report


Directory: ../
File: src/SourceFile.cpp
Date: 2025-12-07 00:53:49
Coverage Exec Excl Total
Lines: 85.1% 444 3 525
Functions: 93.0% 40 0 43
Branches: 46.3% 463 12 1012

Line Branch Exec Source
1 // Copyright (c) 2021-2025 ChilliBits. All rights reserved.
2
3 #include "SourceFile.h"
4
5 #include "visualizer/DependencyGraphVisualizer.h"
6
7 #include <llvm/IR/Module.h>
8 #include <llvm/MC/TargetRegistry.h>
9
10 #include <ast/ASTBuilder.h>
11 #include <exception/AntlrThrowingErrorListener.h>
12 #include <exception/CompilerError.h>
13 #include <global/GlobalResourceManager.h>
14 #include <global/TypeRegistry.h>
15 #include <importcollector/ImportCollector.h>
16 #include <irgenerator/IRGenerator.h>
17 #include <iroptimizer/IROptimizer.h>
18 #include <linker/BitcodeLinker.h>
19 #include <objectemitter/ObjectEmitter.h>
20 #include <symboltablebuilder/SymbolTable.h>
21 #include <symboltablebuilder/SymbolTableBuilder.h>
22 #include <typechecker/MacroDefs.h>
23 #include <typechecker/TypeChecker.h>
24 #include <util/CompilerWarning.h>
25 #include <util/FileUtil.h>
26 #include <util/SystemUtil.h>
27 #include <util/Timer.h>
28 #include <visualizer/ASTVisualizer.h>
29 #include <visualizer/CSTVisualizer.h>
30
31 namespace spice::compiler {
32
33 1213 SourceFile::SourceFile(GlobalResourceManager &resourceManager, SourceFile *parent, std::string name,
34 1213 const std::filesystem::path &filePath, bool stdFile)
35
1/2
✓ Branch 5 → 6 taken 1213 times.
✗ Branch 5 → 119 not taken.
2426 : name(std::move(name)), filePath(filePath), isStdFile(stdFile), parent(parent),
36
3/4
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 1211 times.
✓ Branch 16 → 17 taken 1213 times.
✗ Branch 16 → 58 not taken.
1213 builder(resourceManager.cliOptions.useLTO ? resourceManager.ltoContext : context), resourceManager(resourceManager),
37
1/2
✓ Branch 11 → 12 taken 1213 times.
✗ Branch 11 → 107 not taken.
3639 cliOptions(resourceManager.cliOptions) {
38 // Deduce fileName and fileDir
39
3/6
✓ Branch 24 → 25 taken 1213 times.
✗ Branch 24 → 63 not taken.
✓ Branch 25 → 26 taken 1213 times.
✗ Branch 25 → 61 not taken.
✓ Branch 26 → 27 taken 1213 times.
✗ Branch 26 → 59 not taken.
1213 fileName = std::filesystem::path(filePath).filename().string();
40
3/6
✓ Branch 31 → 32 taken 1213 times.
✗ Branch 31 → 70 not taken.
✓ Branch 32 → 33 taken 1213 times.
✗ Branch 32 → 68 not taken.
✓ Branch 33 → 34 taken 1213 times.
✗ Branch 33 → 66 not taken.
1213 fileDir = std::filesystem::path(filePath).parent_path().string();
41
42 // Search after the selected target
43 1213 std::string error;
44
1/2
✓ Branch 39 → 40 taken 1213 times.
✗ Branch 39 → 86 not taken.
1213 const llvm::Target *target = llvm::TargetRegistry::lookupTarget(cliOptions.targetTriple, error);
45
1/2
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 46 taken 1213 times.
1213 if (!target)
46 throw CompilerError(TARGET_NOT_AVAILABLE, "Selected target was not found: " + error); // GCOV_EXCL_LINE
47
48 // Create the target machine
49
1/2
✓ Branch 46 → 47 taken 1213 times.
✗ Branch 46 → 86 not taken.
1213 llvm::TargetOptions opt;
50 1213 opt.MCOptions.AsmVerbose = true;
51 1213 opt.MCOptions.PreserveAsmComments = true;
52 1213 const std::string &cpuName = resourceManager.cpuName;
53 1213 const std::string &features = resourceManager.cpuFeatures;
54 1213 const llvm::Triple &targetTriple = cliOptions.targetTriple;
55
1/2
✓ Branch 51 → 52 taken 1213 times.
✗ Branch 51 → 79 not taken.
1213 llvm::TargetMachine *targetMachineRaw = target->createTargetMachine(targetTriple, cpuName, features, opt, llvm::Reloc::PIC_);
56 1213 targetMachine = std::unique_ptr<llvm::TargetMachine>(targetMachineRaw);
57 1213 }
58
59 1420 void SourceFile::runLexer() {
60
2/2
✓ Branch 2 → 3 taken 437 times.
✓ Branch 2 → 4 taken 983 times.
1420 if (isMainFile)
61
1/2
✓ Branch 3 → 4 taken 437 times.
✗ Branch 3 → 89 not taken.
437 resourceManager.totalTimer.start();
62
63 // Check if this stage has already been done
64
2/2
✓ Branch 4 → 5 taken 209 times.
✓ Branch 4 → 6 taken 1211 times.
1420 if (previousStage >= LEXER)
65 209 return;
66
67
1/2
✓ Branch 6 → 7 taken 1211 times.
✗ Branch 6 → 89 not taken.
1211 Timer timer(&compilerOutput.times.lexer);
68
1/2
✓ Branch 7 → 8 taken 1211 times.
✗ Branch 7 → 89 not taken.
1211 timer.start();
69
70 // Read from the input source file
71
1/2
✓ Branch 8 → 9 taken 1211 times.
✗ Branch 8 → 89 not taken.
1211 std::ifstream fileInputStream(filePath);
72
3/4
✓ Branch 9 → 10 taken 1211 times.
✗ Branch 9 → 87 not taken.
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 20 taken 1210 times.
1211 if (!fileInputStream)
73
4/8
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 65 not taken.
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 63 not taken.
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 61 not taken.
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 58 not taken.
1 throw CompilerError(SOURCE_FILE_NOT_FOUND, "Source file at path '" + filePath.string() + "' does not exist.");
74
75 // Tokenize input
76
1/2
✓ Branch 20 → 21 taken 1210 times.
✗ Branch 20 → 70 not taken.
1210 antlrCtx.inputStream = std::make_unique<antlr4::ANTLRInputStream>(fileInputStream);
77
1/2
✓ Branch 24 → 25 taken 1210 times.
✗ Branch 24 → 71 not taken.
1210 antlrCtx.lexer = std::make_unique<SpiceLexer>(antlrCtx.inputStream.get());
78
1/2
✓ Branch 28 → 29 taken 1210 times.
✗ Branch 28 → 87 not taken.
1210 antlrCtx.lexer->removeErrorListeners();
79
1/2
✓ Branch 29 → 30 taken 1210 times.
✗ Branch 29 → 73 not taken.
1210 antlrCtx.lexerErrorHandler = std::make_unique<AntlrThrowingErrorListener>(ThrowingErrorListenerMode::LEXER, this);
80
1/2
✓ Branch 34 → 35 taken 1210 times.
✗ Branch 34 → 87 not taken.
1210 antlrCtx.lexer->addErrorListener(antlrCtx.lexerErrorHandler.get());
81
1/2
✓ Branch 36 → 37 taken 1210 times.
✗ Branch 36 → 76 not taken.
1210 antlrCtx.tokenStream = std::make_unique<antlr4::CommonTokenStream>(antlrCtx.lexer.get());
82
83 // Calculate cache key
84
1/2
✓ Branch 39 → 40 taken 1210 times.
✗ Branch 39 → 87 not taken.
1210 std::stringstream cacheKeyString;
85
4/6
✓ Branch 40 → 41 taken 1210 times.
✗ Branch 40 → 85 not taken.
✓ Branch 42 → 43 taken 1209 times.
✓ Branch 42 → 80 taken 1 time.
✓ Branch 44 → 45 taken 1209 times.
✗ Branch 44 → 78 not taken.
1210 cacheKeyString << std::hex << std::hash<std::string>{}(antlrCtx.tokenStream->getText());
86
1/2
✓ Branch 46 → 47 taken 1209 times.
✗ Branch 46 → 82 not taken.
1209 cacheKey = cacheKeyString.str();
87
88 // Try to load from cache
89
1/2
✗ Branch 49 → 50 not taken.
✓ Branch 49 → 52 taken 1209 times.
1209 if (!cliOptions.ignoreCache)
90 restoredFromCache = resourceManager.cacheManager.lookupSourceFile(this);
91
92 1209 previousStage = LEXER;
93
1/2
✓ Branch 52 → 53 taken 1209 times.
✗ Branch 52 → 85 not taken.
1209 timer.stop();
94
1/2
✓ Branch 53 → 54 taken 1209 times.
✗ Branch 53 → 83 not taken.
1209 printStatusMessage("Lexer", IO_CODE, IO_TOKENS, compilerOutput.times.lexer);
95 1212 }
96
97 1418 void SourceFile::runParser() {
98 // Skip if restored from the cache or this stage has already been done
99
3/4
✓ Branch 2 → 3 taken 1418 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 209 times.
✓ Branch 3 → 5 taken 1209 times.
1418 if (restoredFromCache || previousStage >= PARSER)
100 209 return;
101
102
1/2
✓ Branch 5 → 6 taken 1209 times.
✗ Branch 5 → 32 not taken.
1209 Timer timer(&compilerOutput.times.parser);
103
1/2
✓ Branch 6 → 7 taken 1209 times.
✗ Branch 6 → 32 not taken.
1209 timer.start();
104
105 // Parse input
106
1/2
✓ Branch 8 → 9 taken 1209 times.
✗ Branch 8 → 25 not taken.
1209 antlrCtx.parser = std::make_unique<SpiceParser>(antlrCtx.tokenStream.get()); // Check for syntax errors
107
1/2
✓ Branch 12 → 13 taken 1209 times.
✗ Branch 12 → 32 not taken.
1209 antlrCtx.parser->removeErrorListeners();
108
1/2
✓ Branch 13 → 14 taken 1209 times.
✗ Branch 13 → 27 not taken.
1209 antlrCtx.parserErrorHandler = std::make_unique<AntlrThrowingErrorListener>(ThrowingErrorListenerMode::PARSER, this);
109
1/2
✓ Branch 18 → 19 taken 1209 times.
✗ Branch 18 → 32 not taken.
1209 antlrCtx.parser->addErrorListener(antlrCtx.parserErrorHandler.get());
110
1/2
✓ Branch 20 → 21 taken 1209 times.
✗ Branch 20 → 32 not taken.
1209 antlrCtx.parser->removeParseListeners();
111
112 1209 previousStage = PARSER;
113
1/2
✓ Branch 21 → 22 taken 1209 times.
✗ Branch 21 → 32 not taken.
1209 timer.stop();
114
1/2
✓ Branch 22 → 23 taken 1209 times.
✗ Branch 22 → 30 not taken.
1209 printStatusMessage("Parser", IO_TOKENS, IO_CST, compilerOutput.times.parser);
115 }
116
117 991 void SourceFile::runCSTVisualizer() {
118 // Only execute if enabled
119
3/6
✓ Branch 2 → 3 taken 991 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 991 times.
✗ Branch 3 → 6 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 991 times.
991 if (restoredFromCache || (!cliOptions.dump.dumpCST && !cliOptions.testMode))
120 209 return;
121 // Check if this stage has already been done
122
2/2
✓ Branch 6 → 7 taken 209 times.
✓ Branch 6 → 8 taken 782 times.
991 if (previousStage >= CST_VISUALIZER)
123 209 return;
124
125
1/2
✓ Branch 8 → 9 taken 782 times.
✗ Branch 8 → 66 not taken.
782 Timer timer(&compilerOutput.times.cstVisualizer);
126
1/2
✓ Branch 9 → 10 taken 782 times.
✗ Branch 9 → 66 not taken.
782 timer.start();
127
128 // Generate dot code for this source file
129
1/2
✓ Branch 10 → 11 taken 782 times.
✗ Branch 10 → 66 not taken.
782 std::stringstream dotCode;
130
1/2
✓ Branch 11 → 12 taken 782 times.
✗ Branch 11 → 64 not taken.
782 visualizerPreamble(dotCode);
131
1/2
✓ Branch 14 → 15 taken 782 times.
✗ Branch 14 → 64 not taken.
782 CSTVisualizer cstVisualizer(resourceManager, this, antlrCtx.lexer.get(), antlrCtx.parser.get());
132
6/12
✓ Branch 15 → 16 taken 782 times.
✗ Branch 15 → 62 not taken.
✓ Branch 17 → 18 taken 782 times.
✗ Branch 17 → 51 not taken.
✓ Branch 18 → 19 taken 782 times.
✗ Branch 18 → 51 not taken.
✓ Branch 19 → 20 taken 782 times.
✗ Branch 19 → 49 not taken.
✓ Branch 20 → 21 taken 782 times.
✗ Branch 20 → 47 not taken.
✓ Branch 21 → 22 taken 782 times.
✗ Branch 21 → 47 not taken.
782 dotCode << " " << std::any_cast<std::string>(cstVisualizer.visit(antlrCtx.parser->entry())) << "}";
133
1/2
✓ Branch 25 → 26 taken 782 times.
✗ Branch 25 → 62 not taken.
782 antlrCtx.parser->reset();
134
135 // Dump the serialized CST string and the SVG file
136
2/4
✓ Branch 26 → 27 taken 782 times.
✗ Branch 26 → 28 not taken.
✓ Branch 27 → 28 taken 782 times.
✗ Branch 27 → 32 not taken.
782 if (cliOptions.dump.dumpCST || cliOptions.testMode)
137
1/2
✓ Branch 28 → 29 taken 782 times.
✗ Branch 28 → 53 not taken.
782 compilerOutput.cstString = dotCode.str();
138
139
1/2
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 40 taken 782 times.
782 if (cliOptions.dump.dumpCST)
140 visualizerOutput("CST", compilerOutput.cstString);
141
142 782 previousStage = CST_VISUALIZER;
143
1/2
✓ Branch 40 → 41 taken 782 times.
✗ Branch 40 → 62 not taken.
782 timer.stop();
144
1/2
✓ Branch 41 → 42 taken 782 times.
✗ Branch 41 → 60 not taken.
782 printStatusMessage("CST Visualizer", IO_CST, IO_CST, compilerOutput.times.cstVisualizer);
145 782 }
146
147 1418 void SourceFile::runASTBuilder() {
148 // Skip if restored from the cache or this stage has already been done
149
3/4
✓ Branch 2 → 3 taken 1418 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 209 times.
✓ Branch 3 → 5 taken 1209 times.
1418 if (restoredFromCache || previousStage >= AST_BUILDER)
150 209 return;
151
152
1/2
✓ Branch 5 → 6 taken 1209 times.
✗ Branch 5 → 36 not taken.
1209 Timer timer(&compilerOutput.times.astBuilder);
153
1/2
✓ Branch 6 → 7 taken 1209 times.
✗ Branch 6 → 36 not taken.
1209 timer.start();
154
155 // Build AST for this source file
156
1/2
✓ Branch 8 → 9 taken 1209 times.
✗ Branch 8 → 36 not taken.
1209 ASTBuilder astBuilder(resourceManager, this, antlrCtx.inputStream.get());
157
5/6
✓ Branch 10 → 11 taken 1207 times.
✓ Branch 10 → 26 taken 2 times.
✓ Branch 11 → 12 taken 1201 times.
✓ Branch 11 → 26 taken 6 times.
✓ Branch 12 → 13 taken 1201 times.
✗ Branch 12 → 24 not taken.
1209 ast = std::any_cast<EntryNode *>(astBuilder.visit(antlrCtx.parser->entry()));
158
1/2
✓ Branch 15 → 16 taken 1201 times.
✗ Branch 15 → 34 not taken.
1201 antlrCtx.parser->reset();
159
160 // Create global scope
161
1/2
✓ Branch 16 → 17 taken 1201 times.
✗ Branch 16 → 27 not taken.
1201 globalScope = std::make_unique<Scope>(nullptr, this, ScopeType::GLOBAL, &ast->codeLoc);
162
163 1201 previousStage = AST_BUILDER;
164
1/2
✓ Branch 19 → 20 taken 1201 times.
✗ Branch 19 → 34 not taken.
1201 timer.stop();
165
1/2
✓ Branch 20 → 21 taken 1201 times.
✗ Branch 20 → 32 not taken.
1201 printStatusMessage("AST Builder", IO_CST, IO_AST, compilerOutput.times.astBuilder);
166 1209 }
167
168 991 void SourceFile::runASTVisualizer() {
169 // Only execute if enabled
170
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 991 times.
991 if (restoredFromCache)
171 209 return;
172
2/4
✓ Branch 4 → 5 taken 991 times.
✗ Branch 4 → 7 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 991 times.
991 if (!cliOptions.dump.dumpAST && !cliOptions.testMode)
173 return;
174 // Check if this stage has already been done
175
2/2
✓ Branch 7 → 8 taken 209 times.
✓ Branch 7 → 9 taken 782 times.
991 if (previousStage >= AST_VISUALIZER)
176 209 return;
177
178
1/2
✓ Branch 9 → 10 taken 782 times.
✗ Branch 9 → 58 not taken.
782 Timer timer(&compilerOutput.times.astVisualizer);
179
1/2
✓ Branch 10 → 11 taken 782 times.
✗ Branch 10 → 58 not taken.
782 timer.start();
180
181 // Generate dot code for this source file
182
1/2
✓ Branch 11 → 12 taken 782 times.
✗ Branch 11 → 58 not taken.
782 std::stringstream dotCode;
183
1/2
✓ Branch 12 → 13 taken 782 times.
✗ Branch 12 → 56 not taken.
782 visualizerPreamble(dotCode);
184
1/2
✓ Branch 13 → 14 taken 782 times.
✗ Branch 13 → 56 not taken.
782 ASTVisualizer astVisualizer(resourceManager, this);
185
5/10
✓ Branch 14 → 15 taken 782 times.
✗ Branch 14 → 54 not taken.
✓ Branch 15 → 16 taken 782 times.
✗ Branch 15 → 43 not taken.
✓ Branch 16 → 17 taken 782 times.
✗ Branch 16 → 41 not taken.
✓ Branch 17 → 18 taken 782 times.
✗ Branch 17 → 39 not taken.
✓ Branch 18 → 19 taken 782 times.
✗ Branch 18 → 39 not taken.
782 dotCode << " " << std::any_cast<std::string>(astVisualizer.visit(ast)) << "}";
186
187 // Dump the serialized AST string and the SVG file
188
1/2
✓ Branch 21 → 22 taken 782 times.
✗ Branch 21 → 45 not taken.
782 compilerOutput.astString = dotCode.str();
189
190
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 32 taken 782 times.
782 if (cliOptions.dump.dumpAST)
191 visualizerOutput("AST", compilerOutput.astString);
192
193 782 previousStage = AST_VISUALIZER;
194
1/2
✓ Branch 32 → 33 taken 782 times.
✗ Branch 32 → 54 not taken.
782 timer.stop();
195
1/2
✓ Branch 33 → 34 taken 782 times.
✗ Branch 33 → 52 not taken.
782 printStatusMessage("AST Visualizer", IO_AST, IO_AST, compilerOutput.times.astVisualizer);
196 782 }
197
198 1410 void SourceFile::runImportCollector() { // NOLINT(misc-no-recursion)
199 // Skip if restored from the cache or this stage has already been done
200
3/4
✓ Branch 2 → 3 taken 1410 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 209 times.
✓ Branch 3 → 5 taken 1201 times.
1410 if (restoredFromCache || previousStage >= IMPORT_COLLECTOR)
201 209 return;
202
203
1/2
✓ Branch 5 → 6 taken 1201 times.
✗ Branch 5 → 30 not taken.
1201 Timer timer(&compilerOutput.times.importCollector);
204
1/2
✓ Branch 6 → 7 taken 1201 times.
✗ Branch 6 → 30 not taken.
1201 timer.start();
205
206 // Collect the imports for this source file
207
1/2
✓ Branch 7 → 8 taken 1201 times.
✗ Branch 7 → 30 not taken.
1201 ImportCollector importCollector(resourceManager, this);
208
2/2
✓ Branch 8 → 9 taken 1196 times.
✓ Branch 8 → 24 taken 5 times.
1201 importCollector.visit(ast);
209
210 1196 previousStage = IMPORT_COLLECTOR;
211
1/2
✓ Branch 10 → 11 taken 1196 times.
✗ Branch 10 → 28 not taken.
1196 timer.stop();
212
213 // Run first part of pipeline for the imported source file
214
5/8
✓ Branch 11 → 12 taken 1196 times.
✗ Branch 11 → 25 not taken.
✓ Branch 12 → 13 taken 1196 times.
✗ Branch 12 → 25 not taken.
✓ Branch 13 → 14 taken 1196 times.
✗ Branch 13 → 25 not taken.
✓ Branch 19 → 15 taken 634 times.
✓ Branch 19 → 20 taken 1194 times.
1828 for (SourceFile *sourceFile : dependencies | std::views::values)
215
2/2
✓ Branch 16 → 17 taken 632 times.
✓ Branch 16 → 25 taken 2 times.
634 sourceFile->runFrontEnd();
216
217
1/2
✓ Branch 20 → 21 taken 1194 times.
✗ Branch 20 → 26 not taken.
1194 printStatusMessage("Import Collector", IO_AST, IO_AST, compilerOutput.times.importCollector);
218 1201 }
219
220 1403 void SourceFile::runSymbolTableBuilder() {
221 // Skip if restored from the cache or this stage has already been done
222
3/4
✓ Branch 2 → 3 taken 1403 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 209 times.
✓ Branch 3 → 5 taken 1194 times.
1403 if (restoredFromCache || previousStage >= SYMBOL_TABLE_BUILDER)
223 209 return;
224
225
1/2
✓ Branch 5 → 6 taken 1194 times.
✗ Branch 5 → 30 not taken.
1194 Timer timer(&compilerOutput.times.symbolTableBuilder);
226
1/2
✓ Branch 6 → 7 taken 1194 times.
✗ Branch 6 → 30 not taken.
1194 timer.start();
227
228 // The symbol tables of all dependencies are present at this point, so we can merge the exported name registries in
229
2/2
✓ Branch 15 → 9 taken 632 times.
✓ Branch 15 → 16 taken 1194 times.
1826 for (const auto &[importName, sourceFile] : dependencies)
230
1/2
✓ Branch 12 → 13 taken 632 times.
✗ Branch 12 → 24 not taken.
632 mergeNameRegistries(*sourceFile, importName);
231
232 // Build symbol table of the current file
233
1/2
✓ Branch 16 → 17 taken 1194 times.
✗ Branch 16 → 30 not taken.
1194 SymbolTableBuilder symbolTableBuilder(resourceManager, this);
234
2/2
✓ Branch 17 → 18 taken 1176 times.
✓ Branch 17 → 25 taken 18 times.
1194 symbolTableBuilder.visit(ast);
235
236 1176 previousStage = SYMBOL_TABLE_BUILDER;
237
1/2
✓ Branch 19 → 20 taken 1176 times.
✗ Branch 19 → 28 not taken.
1176 timer.stop();
238
1/2
✓ Branch 20 → 21 taken 1176 times.
✗ Branch 20 → 26 not taken.
1176 printStatusMessage("Symbol Table Builder", IO_AST, IO_AST, compilerOutput.times.symbolTableBuilder);
239 1194 }
240
241 1384 void SourceFile::runTypeCheckerPre() { // NOLINT(misc-no-recursion)
242 // Skip if restored from the cache or this stage has already been done
243
3/4
✓ Branch 2 → 3 taken 1384 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 209 times.
✓ Branch 3 → 5 taken 1175 times.
1384 if (restoredFromCache || previousStage >= TYPE_CHECKER_PRE)
244 209 return;
245
246 // Type-check all dependencies first
247
5/8
✓ Branch 5 → 6 taken 1175 times.
✗ Branch 5 → 24 not taken.
✓ Branch 6 → 7 taken 1175 times.
✗ Branch 6 → 24 not taken.
✓ Branch 7 → 8 taken 1175 times.
✗ Branch 7 → 24 not taken.
✓ Branch 13 → 9 taken 631 times.
✓ Branch 13 → 14 taken 1175 times.
1806 for (SourceFile *sourceFile : dependencies | std::views::values)
248
1/2
✓ Branch 10 → 11 taken 631 times.
✗ Branch 10 → 24 not taken.
631 sourceFile->runTypeCheckerPre();
249
250
1/2
✓ Branch 14 → 15 taken 1175 times.
✗ Branch 14 → 30 not taken.
1175 Timer timer(&compilerOutput.times.typeCheckerPre);
251
1/2
✓ Branch 15 → 16 taken 1175 times.
✗ Branch 15 → 30 not taken.
1175 timer.start();
252
253 // Then type-check the current file
254
1/2
✓ Branch 16 → 17 taken 1175 times.
✗ Branch 16 → 30 not taken.
1175 TypeChecker typeChecker(resourceManager, this, TC_MODE_PRE);
255
2/2
✓ Branch 17 → 18 taken 1161 times.
✓ Branch 17 → 25 taken 14 times.
1175 typeChecker.visit(ast);
256
257 1161 previousStage = TYPE_CHECKER_PRE;
258
1/2
✓ Branch 19 → 20 taken 1161 times.
✗ Branch 19 → 28 not taken.
1161 timer.stop();
259
1/2
✓ Branch 20 → 21 taken 1161 times.
✗ Branch 20 → 26 not taken.
1161 printStatusMessage("Type Checker Pre", IO_AST, IO_AST, compilerOutput.times.typeCheckerPre);
260 1175 }
261
262 2912 void SourceFile::runTypeCheckerPost() { // NOLINT(misc-no-recursion)
263 // Skip if restored from cache, this stage has already been done, or not all dependants finished type checking
264
6/8
✓ Branch 2 → 3 taken 2912 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 2912 times.
✗ Branch 3 → 80 not taken.
✓ Branch 4 → 5 taken 538 times.
✓ Branch 4 → 6 taken 2374 times.
✓ Branch 7 → 8 taken 538 times.
✓ Branch 7 → 9 taken 2374 times.
2912 if (restoredFromCache || !haveAllDependantsBeenTypeChecked())
265 538 return;
266
267
1/2
✓ Branch 9 → 10 taken 2374 times.
✗ Branch 9 → 80 not taken.
2374 Timer timer(&compilerOutput.times.typeCheckerPost);
268
1/2
✓ Branch 10 → 11 taken 2374 times.
✗ Branch 10 → 80 not taken.
2374 timer.start();
269
270 // Start type-checking loop. The type-checker can request a re-execution. The max number of type-checker runs is limited
271
1/2
✓ Branch 11 → 12 taken 2374 times.
✗ Branch 11 → 80 not taken.
2374 TypeChecker typeChecker(resourceManager, this, TC_MODE_POST);
272 2374 unsigned short typeCheckerRuns = 0;
273
2/2
✓ Branch 27 → 13 taken 1618 times.
✓ Branch 27 → 28 taken 2311 times.
3929 while (reVisitRequested) {
274 1618 typeCheckerRuns++;
275 1618 totalTypeCheckerRuns++;
276 1618 reVisitRequested = false;
277
278 // Type-check the current file first. Multiple times, if requested
279 1618 timer.resume();
280
2/2
✓ Branch 14 → 15 taken 1586 times.
✓ Branch 14 → 58 taken 32 times.
1618 typeChecker.visit(ast);
281
1/2
✓ Branch 16 → 17 taken 1586 times.
✗ Branch 16 → 78 not taken.
1586 timer.pause();
282
283 // Then type-check all dependencies
284
5/8
✓ Branch 17 → 18 taken 1586 times.
✗ Branch 17 → 59 not taken.
✓ Branch 18 → 19 taken 1586 times.
✗ Branch 18 → 59 not taken.
✓ Branch 19 → 20 taken 1586 times.
✗ Branch 19 → 59 not taken.
✓ Branch 25 → 21 taken 2522 times.
✓ Branch 25 → 26 taken 1555 times.
4077 for (SourceFile *sourceFile : dependencies | std::views::values)
285
2/2
✓ Branch 22 → 23 taken 2491 times.
✓ Branch 22 → 59 taken 31 times.
2522 sourceFile->runTypeCheckerPost();
286 }
287
288
2/2
✓ Branch 28 → 29 taken 2209 times.
✓ Branch 28 → 78 taken 102 times.
2311 checkForSoftErrors();
289
290 // Check if all dyn variables were type-inferred successfully
291
2/2
✓ Branch 30 → 31 taken 2208 times.
✓ Branch 30 → 78 taken 1 time.
2209 globalScope->ensureSuccessfulTypeInference();
292
293 2208 previousStage = TYPE_CHECKER_POST;
294
1/2
✓ Branch 31 → 32 taken 2208 times.
✗ Branch 31 → 78 not taken.
2208 timer.stop();
295
1/2
✓ Branch 32 → 33 taken 2208 times.
✗ Branch 32 → 60 not taken.
2208 printStatusMessage("Type Checker Post", IO_AST, IO_AST, compilerOutput.times.typeCheckerPost, typeCheckerRuns);
296
297 // Save the JSON version in the compiler output
298
2/4
✓ Branch 33 → 34 taken 2208 times.
✗ Branch 33 → 35 not taken.
✓ Branch 34 → 35 taken 2208 times.
✗ Branch 34 → 42 not taken.
2208 if (cliOptions.dump.dumpSymbolTable || cliOptions.testMode)
299
2/4
✓ Branch 36 → 37 taken 2208 times.
✗ Branch 36 → 64 not taken.
✓ Branch 37 → 38 taken 2208 times.
✗ Branch 37 → 62 not taken.
2208 compilerOutput.symbolTableString = globalScope->getSymbolTableJSON().dump(/*indent=*/2);
300
301 // Dump symbol table
302
1/2
✗ Branch 42 → 43 not taken.
✓ Branch 42 → 55 taken 2208 times.
2208 if (cliOptions.dump.dumpSymbolTable)
303 dumpOutput(compilerOutput.symbolTableString, "Symbol Table", "symbol-table.json");
304 2374 }
305
306 257 void SourceFile::runDependencyGraphVisualizer() {
307 // Only execute if enabled
308
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 257 times.
257 if (restoredFromCache)
309 2 return;
310
2/4
✓ Branch 4 → 5 taken 257 times.
✗ Branch 4 → 7 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 257 times.
257 if (!cliOptions.dump.dumpDependencyGraph && !cliOptions.testMode)
311 return;
312 // Check if this stage has already been done
313
2/2
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 9 taken 255 times.
257 if (previousStage >= DEP_GRAPH_VISUALIZER)
314 2 return;
315
316
1/2
✓ Branch 9 → 10 taken 255 times.
✗ Branch 9 → 47 not taken.
255 Timer timer(&compilerOutput.times.depGraphVisualizer);
317
1/2
✓ Branch 10 → 11 taken 255 times.
✗ Branch 10 → 47 not taken.
255 timer.start();
318
319 // Generate dot code for this source file
320
1/2
✓ Branch 11 → 12 taken 255 times.
✗ Branch 11 → 47 not taken.
255 std::stringstream dotCode;
321
1/2
✓ Branch 12 → 13 taken 255 times.
✗ Branch 12 → 45 not taken.
255 visualizerPreamble(dotCode);
322
1/2
✓ Branch 13 → 14 taken 255 times.
✗ Branch 13 → 45 not taken.
255 DependencyGraphVisualizer depGraphVisualizer(resourceManager, this);
323
1/2
✓ Branch 14 → 15 taken 255 times.
✗ Branch 14 → 43 not taken.
255 depGraphVisualizer.getDependencyGraph(dotCode);
324
1/2
✓ Branch 15 → 16 taken 255 times.
✗ Branch 15 → 43 not taken.
255 dotCode << "}";
325
326 // Dump the serialized AST string and the SVG file
327
1/2
✓ Branch 16 → 17 taken 255 times.
✗ Branch 16 → 34 not taken.
255 compilerOutput.depGraphString = dotCode.str();
328
329
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 27 taken 255 times.
255 if (cliOptions.dump.dumpDependencyGraph)
330 visualizerOutput("Dependency Graph", compilerOutput.depGraphString);
331
332 255 previousStage = DEP_GRAPH_VISUALIZER;
333
1/2
✓ Branch 27 → 28 taken 255 times.
✗ Branch 27 → 43 not taken.
255 timer.stop();
334
1/2
✓ Branch 28 → 29 taken 255 times.
✗ Branch 28 → 41 not taken.
255 printStatusMessage("AST Visualizer", IO_AST, IO_AST, compilerOutput.times.depGraphVisualizer);
335 255 }
336
337 3569 void SourceFile::runIRGenerator() {
338 // Skip if restored from the cache or this stage has already been done
339
3/4
✓ Branch 2 → 3 taken 3569 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 2598 times.
✓ Branch 3 → 5 taken 971 times.
3569 if (restoredFromCache || previousStage >= IR_GENERATOR)
340 2598 return;
341
342
1/2
✓ Branch 5 → 6 taken 971 times.
✗ Branch 5 → 76 not taken.
971 Timer timer(&compilerOutput.times.irGenerator);
343
1/2
✓ Branch 6 → 7 taken 971 times.
✗ Branch 6 → 76 not taken.
971 timer.start();
344
345 // Create the LLVM module for this source file
346
2/2
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 9 taken 969 times.
971 llvm::LLVMContext &llvmContext = cliOptions.useLTO ? resourceManager.ltoContext : context;
347
1/2
✓ Branch 10 → 11 taken 971 times.
✗ Branch 10 → 50 not taken.
971 llvmModule = std::make_unique<llvm::Module>(fileName, llvmContext);
348
349 // Generate this source file
350
1/2
✓ Branch 13 → 14 taken 971 times.
✗ Branch 13 → 76 not taken.
971 IRGenerator irGenerator(resourceManager, this);
351
1/2
✓ Branch 14 → 15 taken 971 times.
✗ Branch 14 → 51 not taken.
971 irGenerator.visit(ast);
352
353 // Print warning if the verifier is disabled
354
4/4
✓ Branch 16 → 17 taken 255 times.
✓ Branch 16 → 25 taken 716 times.
✓ Branch 17 → 18 taken 1 time.
✓ Branch 17 → 25 taken 254 times.
971 if (isMainFile && cliOptions.disableVerifier) {
355
1/2
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 52 not taken.
1 const std::string warningMessage = "The LLVM verifier passes are disabled. Please use this cli option with caution.";
356
1/2
✓ Branch 22 → 23 taken 1 time.
✗ Branch 22 → 55 not taken.
1 compilerOutput.warnings.emplace_back(VERIFIER_DISABLED, warningMessage);
357 1 }
358
359 // Save the ir string in the compiler output
360
2/4
✓ Branch 25 → 26 taken 971 times.
✗ Branch 25 → 27 not taken.
✓ Branch 26 → 27 taken 971 times.
✗ Branch 26 → 32 not taken.
971 if (cliOptions.dump.dumpIR || cliOptions.testMode)
361
1/2
✓ Branch 28 → 29 taken 971 times.
✗ Branch 28 → 59 not taken.
971 compilerOutput.irString = IRGenerator::getIRString(llvmModule.get(), cliOptions);
362
363 // Dump unoptimized IR code
364
1/2
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 45 taken 971 times.
971 if (cliOptions.dump.dumpIR)
365 dumpOutput(compilerOutput.irString, "Unoptimized IR Code", "ir-code.ll");
366
367 971 previousStage = IR_GENERATOR;
368
1/2
✓ Branch 45 → 46 taken 971 times.
✗ Branch 45 → 74 not taken.
971 timer.stop();
369
1/2
✓ Branch 46 → 47 taken 971 times.
✗ Branch 46 → 72 not taken.
971 printStatusMessage("IR Generator", IO_AST, IO_IR, compilerOutput.times.irGenerator);
370 971 }
371
372 3489 void SourceFile::runDefaultIROptimizer() {
373
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3489 times.
3489 assert(!cliOptions.useLTO);
374
375 // Skip if restored from the cache or this stage has already been done
376
6/8
✓ Branch 4 → 5 taken 3489 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 891 times.
✓ Branch 5 → 8 taken 2598 times.
✓ Branch 6 → 7 taken 26 times.
✓ Branch 6 → 9 taken 865 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 26 times.
3489 if (restoredFromCache || previousStage > IR_OPTIMIZER || (previousStage == IR_OPTIMIZER && !cliOptions.testMode))
377 2598 return;
378
379
1/2
✓ Branch 9 → 10 taken 891 times.
✗ Branch 9 → 60 not taken.
891 Timer timer(&compilerOutput.times.irOptimizer);
380
1/2
✓ Branch 10 → 11 taken 891 times.
✗ Branch 10 → 60 not taken.
891 timer.start();
381
382 // Optimize this source file
383
1/2
✓ Branch 11 → 12 taken 891 times.
✗ Branch 11 → 60 not taken.
891 IROptimizer irOptimizer(resourceManager, this);
384
1/2
✓ Branch 12 → 13 taken 891 times.
✗ Branch 12 → 58 not taken.
891 irOptimizer.prepare();
385
1/2
✓ Branch 13 → 14 taken 891 times.
✗ Branch 13 → 58 not taken.
891 irOptimizer.optimizeDefault();
386
387 // Save the optimized ir string in the compiler output
388
2/4
✓ Branch 14 → 15 taken 891 times.
✗ Branch 14 → 16 not taken.
✓ Branch 15 → 16 taken 891 times.
✗ Branch 15 → 21 not taken.
891 if (cliOptions.dump.dumpIR || cliOptions.testMode)
389
1/2
✓ Branch 17 → 18 taken 891 times.
✗ Branch 17 → 40 not taken.
891 compilerOutput.irOptString = IRGenerator::getIRString(llvmModule.get(), cliOptions);
390
391 // Dump optimized IR code
392
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 35 taken 891 times.
891 if (cliOptions.dump.dumpIR)
393 dumpOutput(compilerOutput.irOptString, "Optimized IR Code",
394 "ir-code-O" + std::to_string(static_cast<uint8_t>(cliOptions.optLevel)) + ".ll");
395
396 891 previousStage = IR_OPTIMIZER;
397
1/2
✓ Branch 35 → 36 taken 891 times.
✗ Branch 35 → 58 not taken.
891 timer.stop();
398
1/2
✓ Branch 36 → 37 taken 891 times.
✗ Branch 36 → 56 not taken.
891 printStatusMessage("IR Optimizer", IO_IR, IO_IR, compilerOutput.times.irOptimizer);
399 891 }
400
401 2 void SourceFile::runPreLinkIROptimizer() {
402
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2 times.
2 assert(cliOptions.useLTO);
403
404 // Skip if restored from the cache or this stage has already been done
405
2/4
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 6 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 2 times.
2 if (restoredFromCache || previousStage >= IR_OPTIMIZER)
406 return;
407
408
1/2
✓ Branch 7 → 8 taken 2 times.
✗ Branch 7 → 51 not taken.
2 Timer timer(&compilerOutput.times.irOptimizer);
409
1/2
✓ Branch 8 → 9 taken 2 times.
✗ Branch 8 → 51 not taken.
2 timer.start();
410
411 // Optimize this source file
412
1/2
✓ Branch 9 → 10 taken 2 times.
✗ Branch 9 → 51 not taken.
2 IROptimizer irOptimizer(resourceManager, this);
413
1/2
✓ Branch 10 → 11 taken 2 times.
✗ Branch 10 → 49 not taken.
2 irOptimizer.prepare();
414
1/2
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 49 not taken.
2 irOptimizer.optimizePreLink();
415
416 // Save the optimized ir string in the compiler output
417
2/4
✓ Branch 12 → 13 taken 2 times.
✗ Branch 12 → 14 not taken.
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 19 not taken.
2 if (cliOptions.dump.dumpIR || cliOptions.testMode)
418
1/2
✓ Branch 15 → 16 taken 2 times.
✗ Branch 15 → 36 not taken.
2 compilerOutput.irOptString = IRGenerator::getIRString(llvmModule.get(), cliOptions);
419
420 // Dump optimized IR code
421
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 32 taken 2 times.
2 if (cliOptions.dump.dumpIR)
422 dumpOutput(compilerOutput.irOptString, "Optimized IR Code (pre-link)", "ir-code-lto-pre-link.ll");
423
424
1/2
✓ Branch 32 → 33 taken 2 times.
✗ Branch 32 → 49 not taken.
2 timer.pause();
425 2 }
426
427 2 void SourceFile::runBitcodeLinker() {
428
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2 times.
2 assert(cliOptions.useLTO);
429
430 // Skip if this is not the main source file
431
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 1 time.
2 if (!isMainFile)
432 1 return;
433
434 // Skip if restored from the cache or this stage has already been done
435
2/4
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 8 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1 time.
1 if (restoredFromCache || previousStage >= IR_OPTIMIZER)
436 return;
437
438
1/2
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 20 not taken.
1 Timer timer(&compilerOutput.times.irOptimizer);
439 1 timer.resume();
440
441 // Link all source files together
442
1/2
✓ Branch 11 → 12 taken 1 time.
✗ Branch 11 → 20 not taken.
1 BitcodeLinker linker(resourceManager);
443
1/2
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 18 not taken.
1 linker.link();
444
445
1/2
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 18 not taken.
1 timer.pause();
446 1 }
447
448 2 void SourceFile::runPostLinkIROptimizer() {
449
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2 times.
2 assert(cliOptions.useLTO);
450
451 // Skip if this is not the main source file
452
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 1 time.
2 if (!isMainFile)
453 1 return;
454
455 // Skip if restored from the cache or this stage has already been done
456
2/4
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 8 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1 time.
1 if (restoredFromCache || previousStage >= IR_OPTIMIZER)
457 return;
458
459
1/2
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 57 not taken.
1 Timer timer(&compilerOutput.times.irOptimizer);
460 1 timer.resume();
461
462 // Optimize LTO module
463
1/2
✓ Branch 11 → 12 taken 1 time.
✗ Branch 11 → 57 not taken.
1 IROptimizer irOptimizer(resourceManager, this);
464
1/2
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 55 not taken.
1 irOptimizer.prepare();
465
1/2
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 55 not taken.
1 irOptimizer.optimizePostLink();
466
467 // Save the optimized ir string in the compiler output
468
2/4
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 16 not taken.
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 21 not taken.
1 if (cliOptions.dump.dumpIR || cliOptions.testMode) {
469 1 llvm::Module *module = resourceManager.ltoModule.get();
470
1/2
✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 40 not taken.
1 compilerOutput.irOptString = IRGenerator::getIRString(module, cliOptions);
471 }
472
473 // Dump optimized IR code
474
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 34 taken 1 time.
1 if (cliOptions.dump.dumpIR)
475 dumpOutput(compilerOutput.irOptString, "Optimized IR Code (post-Link)", "ir-code-lto-post-link.ll");
476
477 1 previousStage = IR_OPTIMIZER;
478
1/2
✓ Branch 34 → 35 taken 1 time.
✗ Branch 34 → 55 not taken.
1 timer.stop();
479
1/2
✓ Branch 35 → 36 taken 1 time.
✗ Branch 35 → 53 not taken.
1 printStatusMessage("IR Optimizer", IO_IR, IO_IR, compilerOutput.times.irOptimizer);
480 1 }
481
482 3515 void SourceFile::runObjectEmitter() {
483 // Skip if restored from the cache or this stage has already been done
484
3/4
✓ Branch 2 → 3 taken 3515 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 2598 times.
✓ Branch 3 → 5 taken 917 times.
3515 if (restoredFromCache || previousStage >= OBJECT_EMITTER)
485 2599 return;
486
487 // Skip if LTO is enabled and this is not the main source file
488
4/4
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 8 taken 915 times.
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 1 time.
917 if (cliOptions.useLTO && !isMainFile)
489 1 return;
490
491
1/2
✓ Branch 8 → 9 taken 916 times.
✗ Branch 8 → 72 not taken.
916 Timer timer(&compilerOutput.times.objectEmitter);
492
1/2
✓ Branch 9 → 10 taken 916 times.
✗ Branch 9 → 72 not taken.
916 timer.start();
493
494 // Deduce an object file path
495
2/4
✓ Branch 10 → 11 taken 916 times.
✗ Branch 10 → 47 not taken.
✓ Branch 11 → 12 taken 916 times.
✗ Branch 11 → 45 not taken.
916 std::filesystem::path objectFilePath = cliOptions.outputDir / filePath.filename();
496
2/4
✓ Branch 13 → 14 taken 916 times.
✗ Branch 13 → 50 not taken.
✓ Branch 14 → 15 taken 916 times.
✗ Branch 14 → 48 not taken.
916 objectFilePath.replace_extension("o");
497
498 // Emit object for this source file
499
1/2
✓ Branch 16 → 17 taken 916 times.
✗ Branch 16 → 70 not taken.
916 const ObjectEmitter objectEmitter(resourceManager, this);
500
1/2
✓ Branch 17 → 18 taken 916 times.
✗ Branch 17 → 68 not taken.
916 objectEmitter.emit(objectFilePath);
501
502 // Save assembly string in the compiler output
503
4/6
✓ Branch 18 → 19 taken 872 times.
✓ Branch 18 → 22 taken 44 times.
✓ Branch 19 → 20 taken 872 times.
✗ Branch 19 → 21 not taken.
✓ Branch 20 → 21 taken 872 times.
✗ Branch 20 → 22 not taken.
916 if (cliOptions.isNativeTarget && (cliOptions.dump.dumpAssembly || cliOptions.testMode))
504
1/2
✓ Branch 21 → 22 taken 872 times.
✗ Branch 21 → 68 not taken.
872 objectEmitter.getASMString(compilerOutput.asmString);
505
506 // Dump assembly code
507
1/2
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 35 taken 916 times.
916 if (cliOptions.dump.dumpAssembly)
508 dumpOutput(compilerOutput.asmString, "Assembly code", "assembly-code.s");
509
510 // Add the object file to the linker objects
511
2/4
✓ Branch 35 → 36 taken 916 times.
✗ Branch 35 → 65 not taken.
✓ Branch 36 → 37 taken 916 times.
✗ Branch 36 → 63 not taken.
916 resourceManager.linker.addObjectFilePath(objectFilePath.string());
512
513 916 previousStage = OBJECT_EMITTER;
514
1/2
✓ Branch 38 → 39 taken 916 times.
✗ Branch 38 → 68 not taken.
916 timer.stop();
515
1/2
✓ Branch 39 → 40 taken 916 times.
✗ Branch 39 → 66 not taken.
916 printStatusMessage("Object Emitter", IO_IR, IO_OBJECT_FILE, compilerOutput.times.objectEmitter);
516 916 }
517
518 3515 void SourceFile::concludeCompilation() {
519 // Skip if restored from the cache or this stage has already been done
520
3/4
✓ Branch 2 → 3 taken 3515 times.
✗ Branch 2 → 4 not taken.
✓ Branch 3 → 4 taken 2598 times.
✓ Branch 3 → 5 taken 917 times.
3515 if (restoredFromCache || previousStage >= FINISHED)
521 2598 return;
522
523 // Cache the source file
524
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 917 times.
917 if (!cliOptions.ignoreCache)
525 resourceManager.cacheManager.cacheSourceFile(this);
526
527 // Save type registry as string in the compiler output
528
4/6
✓ Branch 7 → 8 taken 201 times.
✓ Branch 7 → 14 taken 716 times.
✓ Branch 8 → 9 taken 201 times.
✗ Branch 8 → 10 not taken.
✓ Branch 9 → 10 taken 201 times.
✗ Branch 9 → 14 not taken.
917 if (isMainFile && (cliOptions.dump.dumpTypes || cliOptions.testMode))
529
1/2
✓ Branch 10 → 11 taken 201 times.
✗ Branch 10 → 52 not taken.
201 compilerOutput.typesString = TypeRegistry::dump();
530
531 // Dump type registry
532
3/4
✓ Branch 14 → 15 taken 201 times.
✓ Branch 14 → 28 taken 716 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 28 taken 201 times.
917 if (isMainFile && cliOptions.dump.dumpTypes)
533 dumpOutput(compilerOutput.typesString, "Type Registry", "type-registry.out");
534
535 // Save cache statistics as string in the compiler output
536
4/6
✓ Branch 28 → 29 taken 201 times.
✓ Branch 28 → 32 taken 716 times.
✓ Branch 29 → 30 taken 201 times.
✗ Branch 29 → 31 not taken.
✓ Branch 30 → 31 taken 201 times.
✗ Branch 30 → 32 not taken.
917 if (isMainFile && (cliOptions.dump.dumpCacheStats || cliOptions.testMode))
537 201 dumpCacheStats();
538
539 // Dump lookup cache statistics
540
3/4
✓ Branch 32 → 33 taken 201 times.
✓ Branch 32 → 46 taken 716 times.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 46 taken 201 times.
917 if (isMainFile && cliOptions.dump.dumpCacheStats)
541 dumpOutput(compilerOutput.cacheStats, "Cache Statistics", "cache-stats.out");
542
543
1/2
✗ Branch 46 → 47 not taken.
✓ Branch 46 → 50 taken 917 times.
917 if (cliOptions.printDebugOutput)
544 std::cout << "Finished compiling " << fileName << std::endl;
545
546 917 previousStage = FINISHED;
547 }
548
549 983 void SourceFile::runFrontEnd() { // NOLINT(misc-no-recursion)
550 983 runLexer();
551
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 983 times.
983 CHECK_ABORT_FLAG_V()
552 983 runParser();
553
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 983 times.
983 CHECK_ABORT_FLAG_V()
554 983 runCSTVisualizer();
555
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 983 times.
983 CHECK_ABORT_FLAG_V()
556 983 runASTBuilder();
557
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 983 times.
983 CHECK_ABORT_FLAG_V()
558 983 runASTVisualizer();
559
1/2
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 983 times.
983 CHECK_ABORT_FLAG_V()
560 983 runImportCollector();
561
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 981 times.
981 CHECK_ABORT_FLAG_V()
562 981 runSymbolTableBuilder();
563
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 981 times.
981 CHECK_ABORT_FLAG_V()
564 }
565
566 404 void SourceFile::runMiddleEnd() {
567 // We need two runs here due to generics.
568 // The first run to determine all concrete function/struct/interface substantiations
569 404 runTypeCheckerPre(); // Visit the dependency tree from bottom to top
570
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 390 times.
390 CHECK_ABORT_FLAG_V()
571 // The second run to ensure, also generic scopes are type-checked properly
572 390 runTypeCheckerPost(); // Visit the dependency tree from top to bottom in topological order
573
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 255 times.
255 CHECK_ABORT_FLAG_V()
574 // Visualize dependency graph
575 255 runDependencyGraphVisualizer();
576
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 255 times.
255 CHECK_ABORT_FLAG_V()
577 }
578
579 3314 void SourceFile::runBackEnd() { // NOLINT(misc-no-recursion)
580 // Run backend for all dependencies first
581
5/8
✓ Branch 2 → 3 taken 3314 times.
✗ Branch 2 → 36 not taken.
✓ Branch 3 → 4 taken 3314 times.
✗ Branch 3 → 36 not taken.
✓ Branch 4 → 5 taken 3314 times.
✗ Branch 4 → 36 not taken.
✓ Branch 10 → 6 taken 3121 times.
✓ Branch 10 → 11 taken 3314 times.
6435 for (SourceFile *sourceFile : dependencies | std::views::values)
582
1/2
✓ Branch 7 → 8 taken 3121 times.
✗ Branch 7 → 36 not taken.
3121 sourceFile->runBackEnd();
583
584 3314 runIRGenerator();
585
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 3314 times.
3314 CHECK_ABORT_FLAG_V()
586
2/2
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 24 taken 3313 times.
3314 if (cliOptions.useLTO) {
587 1 runPreLinkIROptimizer();
588
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 1 time.
1 CHECK_ABORT_FLAG_V()
589 1 runBitcodeLinker();
590
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 1 time.
1 CHECK_ABORT_FLAG_V()
591 1 runPostLinkIROptimizer();
592
1/2
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 27 taken 1 time.
1 CHECK_ABORT_FLAG_V()
593 } else {
594 3313 runDefaultIROptimizer();
595
1/2
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 3313 times.
3313 CHECK_ABORT_FLAG_V()
596 }
597 3314 runObjectEmitter();
598
1/2
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 3314 times.
3314 CHECK_ABORT_FLAG_V()
599 3314 concludeCompilation();
600
601
1/2
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 35 taken 3314 times.
3314 if (isMainFile) {
602 resourceManager.totalTimer.stop();
603 if (cliOptions.printDebugOutput)
604 dumpCompilationStats();
605 }
606 }
607
608 1436 void SourceFile::addDependency(SourceFile *sourceFile, const ASTNode *declNode, const std::string &dependencyName,
609 const std::string &path) {
610 // Check if this would cause a circular dependency
611
1/2
✓ Branch 2 → 3 taken 1436 times.
✗ Branch 2 → 41 not taken.
1436 std::stack<const SourceFile *> dependencyCircle;
612
3/4
✓ Branch 3 → 4 taken 1436 times.
✗ Branch 3 → 39 not taken.
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 17 taken 1435 times.
1436 if (isAlreadyImported(path, dependencyCircle)) {
613 // Build the error message
614
1/2
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 34 not taken.
1 std::stringstream errorMessage;
615
3/6
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 32 not taken.
✓ Branch 7 → 8 taken 1 time.
✗ Branch 7 → 32 not taken.
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 32 not taken.
1 errorMessage << "Circular import detected while importing '" << sourceFile->fileName << "':\n\n";
616
2/4
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 25 not taken.
✓ Branch 10 → 11 taken 1 time.
✗ Branch 10 → 23 not taken.
1 errorMessage << CommonUtil::getCircularImportMessage(dependencyCircle);
617
2/4
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 29 not taken.
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 26 not taken.
1 throw SemanticError(declNode, CIRCULAR_DEPENDENCY, errorMessage.str());
618 1 }
619
620 // Add the dependency
621 1435 sourceFile->isMainFile = false;
622
2/4
✓ Branch 17 → 18 taken 1435 times.
✗ Branch 17 → 37 not taken.
✓ Branch 18 → 19 taken 1435 times.
✗ Branch 18 → 35 not taken.
1435 dependencies.insert({dependencyName, sourceFile});
623
624 // Add the dependant
625
1/2
✓ Branch 20 → 21 taken 1435 times.
✗ Branch 20 → 38 not taken.
1435 sourceFile->dependants.push_back(this);
626 1436 }
627
628 99502 bool SourceFile::imports(const SourceFile *sourceFile) const {
629 308223 return std::ranges::any_of(dependencies, [=](const auto &dependency) { return dependency.second == sourceFile; });
630 }
631
632 6656 bool SourceFile::isAlreadyImported(const std::string &filePathSearch, // NOLINT(misc-no-recursion)
633 std::stack<const SourceFile *> &circle) const {
634
1/2
✓ Branch 2 → 3 taken 6656 times.
✗ Branch 2 → 20 not taken.
6656 circle.push(this);
635
636 // Check if the current source file corresponds to the path to search
637
4/6
✓ Branch 3 → 4 taken 6656 times.
✗ Branch 3 → 23 not taken.
✓ Branch 4 → 5 taken 6656 times.
✗ Branch 4 → 21 not taken.
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 6655 times.
6656 if (std::filesystem::equivalent(filePath, filePathSearch))
638 1 return true;
639
640 // Check dependants recursively
641
2/2
✓ Branch 16 → 10 taken 5220 times.
✓ Branch 16 → 17 taken 6653 times.
11873 for (const SourceFile *dependant : dependants)
642
3/4
✓ Branch 11 → 12 taken 5220 times.
✗ Branch 11 → 24 not taken.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 14 taken 5218 times.
5220 if (dependant->isAlreadyImported(filePathSearch, circle))
643 2 return true;
644
645 // If no dependant was found, remove the current source file from the circle to continue with the next sibling
646 6653 circle.pop();
647 6653 return false;
648 }
649
650 3656 SourceFile *SourceFile::requestRuntimeModule(RuntimeModule runtimeModule) {
651 // Check if the module was already imported
652
2/2
✓ Branch 3 → 4 taken 2857 times.
✓ Branch 3 → 6 taken 799 times.
3656 if (isRuntimeModuleAvailable(runtimeModule))
653 2857 return resourceManager.runtimeModuleManager.getModule(runtimeModule);
654 799 return resourceManager.runtimeModuleManager.requestModule(this, runtimeModule);
655 }
656
657 3992 bool SourceFile::isRuntimeModuleAvailable(RuntimeModule runtimeModule) const { return importedRuntimeModules & runtimeModule; }
658
659 37312 void SourceFile::addNameRegistryEntry(const std::string &symbolName, uint64_t typeId, SymbolTableEntry *entry, Scope *scope,
660 bool keepNewOnCollision, SymbolTableEntry *importEntry) {
661
6/6
✓ Branch 2 → 3 taken 9068 times.
✓ Branch 2 → 5 taken 28244 times.
✓ Branch 4 → 5 taken 8997 times.
✓ Branch 4 → 6 taken 71 times.
✓ Branch 7 → 8 taken 37241 times.
✓ Branch 7 → 13 taken 71 times.
37312 if (keepNewOnCollision || !exportedNameRegistry.contains(symbolName)) // Overwrite potential existing entry
662 37241 exportedNameRegistry[symbolName] = {symbolName, typeId, entry, scope, importEntry};
663 else // Name collision => we must remove the existing entry
664 71 exportedNameRegistry.erase(symbolName);
665
2/6
✓ Branch 8 → 9 taken 37241 times.
✗ Branch 8 → 20 not taken.
✓ Branch 9 → 10 taken 37241 times.
✗ Branch 9 → 15 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 19 not taken.
74553 }
666
667 210186 const NameRegistryEntry *SourceFile::getNameRegistryEntry(const std::string &symbolName) const {
668
2/2
✓ Branch 3 → 4 taken 105687 times.
✓ Branch 3 → 5 taken 104499 times.
210186 if (!exportedNameRegistry.contains(symbolName))
669 105687 return nullptr;
670
671 // Resolve registry entry for the given name
672
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 104499 times.
104499 assert(exportedNameRegistry.contains(symbolName));
673 104499 const NameRegistryEntry *entry = &exportedNameRegistry.at(symbolName);
674
675 // Mark the import entry as used
676
2/2
✓ Branch 9 → 10 taken 5340 times.
✓ Branch 9 → 11 taken 99159 times.
104499 if (entry->importEntry != nullptr)
677 5340 entry->importEntry->used = true;
678
679 104499 return entry;
680 }
681
682 356875 llvm::Type *SourceFile::getLLVMType(const Type *type) {
683 // Check if the type is already in the mapping
684
1/2
✓ Branch 2 → 3 taken 356875 times.
✗ Branch 2 → 13 not taken.
356875 const auto it = typeToLLVMTypeMapping.find(type);
685
2/2
✓ Branch 5 → 6 taken 348504 times.
✓ Branch 5 → 8 taken 8371 times.
356875 if (it != typeToLLVMTypeMapping.end())
686 348504 return it->second;
687
688 // If not, generate the LLVM type
689
1/2
✓ Branch 8 → 9 taken 8371 times.
✗ Branch 8 → 13 not taken.
8371 llvm::Type *llvmType = type->toLLVMType(this);
690
1/2
✓ Branch 9 → 10 taken 8371 times.
✗ Branch 9 → 13 not taken.
8371 typeToLLVMTypeMapping[type] = llvmType;
691 8371 return llvmType;
692 }
693
694 2313 void SourceFile::checkForSoftErrors() const {
695 // Check if there are any soft errors and if so, print them
696
2/2
✓ Branch 3 → 4 taken 104 times.
✓ Branch 3 → 19 taken 2209 times.
2313 if (!resourceManager.errorManager.softErrors.empty()) {
697
1/2
✓ Branch 4 → 5 taken 104 times.
✗ Branch 4 → 29 not taken.
104 std::stringstream errorStream;
698
1/2
✓ Branch 5 → 6 taken 104 times.
✗ Branch 5 → 27 not taken.
104 errorStream << "There are unresolved errors. Please fix them and recompile.";
699
2/2
✓ Branch 13 → 8 taken 126 times.
✓ Branch 13 → 14 taken 104 times.
230 for (const auto &[codeLoc, message] : resourceManager.errorManager.softErrors)
700
2/4
✓ Branch 9 → 10 taken 126 times.
✗ Branch 9 → 20 not taken.
✓ Branch 10 → 11 taken 126 times.
✗ Branch 10 → 20 not taken.
126 errorStream << "\n\n" << message;
701
2/4
✓ Branch 15 → 16 taken 104 times.
✗ Branch 15 → 24 not taken.
✓ Branch 16 → 17 taken 104 times.
✗ Branch 16 → 21 not taken.
104 throw CompilerError(UNRESOLVED_SOFT_ERRORS, errorStream.str());
702 104 }
703 2209 }
704
705 289 void SourceFile::collectAndPrintWarnings() { // NOLINT(misc-no-recursion)
706 // Print warnings for all dependencies
707
5/8
✓ Branch 2 → 3 taken 289 times.
✗ Branch 2 → 23 not taken.
✓ Branch 3 → 4 taken 289 times.
✗ Branch 3 → 23 not taken.
✓ Branch 4 → 5 taken 289 times.
✗ Branch 4 → 23 not taken.
✓ Branch 11 → 6 taken 255 times.
✓ Branch 11 → 12 taken 289 times.
544 for (SourceFile *sourceFile : dependencies | std::views::values)
708
2/2
✓ Branch 7 → 8 taken 34 times.
✓ Branch 7 → 9 taken 221 times.
255 if (!sourceFile->isStdFile)
709
1/2
✓ Branch 8 → 9 taken 34 times.
✗ Branch 8 → 23 not taken.
34 sourceFile->collectAndPrintWarnings();
710 // Collect warnings for this file
711
2/2
✓ Branch 12 → 13 taken 287 times.
✓ Branch 12 → 15 taken 2 times.
289 if (!ignoreWarnings)
712 287 globalScope->collectWarnings(compilerOutput.warnings);
713 // Print warnings for this file
714
2/2
✓ Branch 21 → 17 taken 166 times.
✓ Branch 21 → 22 taken 289 times.
455 for (const CompilerWarning &warning : compilerOutput.warnings)
715
1/2
✓ Branch 18 → 19 taken 166 times.
✗ Branch 18 → 24 not taken.
166 warning.print();
716 289 }
717
718 4299 const SourceFile *SourceFile::getRootSourceFile() const { // NOLINT(misc-no-recursion)
719
2/2
✓ Branch 2 → 3 taken 1522 times.
✓ Branch 2 → 4 taken 2777 times.
4299 return isMainFile ? this : parent->getRootSourceFile();
720 }
721
722 11529 bool SourceFile::isRT(RuntimeModule runtimeModule) const {
723
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 11529 times.
11529 assert(IDENTIFYING_TOP_LEVEL_NAMES.contains(runtimeModule));
724 11529 const char *topLevelName = IDENTIFYING_TOP_LEVEL_NAMES.at(runtimeModule);
725
4/6
✓ Branch 8 → 9 taken 11529 times.
✗ Branch 8 → 26 not taken.
✓ Branch 9 → 10 taken 11529 times.
✗ Branch 9 → 24 not taken.
✓ Branch 12 → 13 taken 1742 times.
✓ Branch 12 → 14 taken 9787 times.
34587 if (!exportedNameRegistry.contains(topLevelName))
726 1742 return false;
727
2/4
✓ Branch 16 → 17 taken 9787 times.
✗ Branch 16 → 32 not taken.
✓ Branch 17 → 18 taken 9787 times.
✗ Branch 17 → 30 not taken.
29361 return exportedNameRegistry.at(topLevelName).targetEntry->scope == globalScope.get();
728 }
729
730 2912 bool SourceFile::haveAllDependantsBeenTypeChecked() const {
731 8747 return std::ranges::all_of(dependants, [](const SourceFile *dependant) { return dependant->totalTypeCheckerRuns >= 1; });
732 }
733
734 /**
735 * Acquire all publicly visible symbols from the imported source file and put them in the name registry of the current one.
736 * But only do that for the symbols that are actually defined in the imported source file. Do not allow transitive dependencies.
737 * Here, we also register privately visible symbols to know that the symbol exist. The error handling regarding the visibility
738 * is issued later in the pipeline.
739 *
740 * @param importedSourceFile Imported source file
741 * @param importName First fragment of all fully qualified symbol names from that import
742 */
743 1431 void SourceFile::mergeNameRegistries(const SourceFile &importedSourceFile, const std::string &importName) {
744 // Retrieve import entry
745 1431 SymbolTableEntry *importEntry = globalScope->lookupStrict(importName);
746
3/4
✓ Branch 6 → 7 taken 799 times.
✓ Branch 6 → 10 taken 632 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 799 times.
1431 assert(importEntry != nullptr || importName.starts_with("__")); // Runtime imports start with two underscores
747
748
2/2
✓ Branch 31 → 12 taken 40080 times.
✓ Branch 31 → 32 taken 1431 times.
41511 for (const auto &[originalName, entry] : importedSourceFile.exportedNameRegistry) {
749 // Skip if we introduce a transitive dependency
750
2/2
✓ Branch 16 → 17 taken 19654 times.
✓ Branch 16 → 18 taken 20426 times.
40080 if (entry.targetScope->sourceFile->globalScope != importedSourceFile.globalScope)
751 19654 continue;
752 // Add the fully qualified name
753
1/2
✓ Branch 18 → 19 taken 20426 times.
✗ Branch 18 → 44 not taken.
20426 std::string newName = importName;
754
1/2
✓ Branch 19 → 20 taken 20426 times.
✗ Branch 19 → 42 not taken.
20426 newName += SCOPE_ACCESS_TOKEN;
755
1/2
✓ Branch 20 → 21 taken 20426 times.
✗ Branch 20 → 42 not taken.
20426 newName += originalName;
756
1/2
✓ Branch 23 → 24 taken 20426 times.
✗ Branch 23 → 33 not taken.
20426 exportedNameRegistry.insert({newName, {newName, entry.typeId, entry.targetEntry, entry.targetScope, importEntry}});
757 // Add the shortened name, considering the name collision
758 20426 const bool keepOnCollision = importedSourceFile.alwaysKeepSymbolsOnNameCollision;
759
1/2
✓ Branch 26 → 27 taken 20426 times.
✗ Branch 26 → 42 not taken.
20426 addNameRegistryEntry(originalName, entry.typeId, entry.targetEntry, entry.targetScope, keepOnCollision, importEntry);
760 20426 }
761
2/6
✓ Branch 21 → 22 taken 20426 times.
✗ Branch 21 → 40 not taken.
✓ Branch 22 → 23 taken 20426 times.
✗ Branch 22 → 35 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 39 not taken.
21857 }
762
763 201 void SourceFile::dumpCacheStats() {
764
1/2
✓ Branch 2 → 3 taken 201 times.
✗ Branch 2 → 32 not taken.
201 std::stringstream cacheStats;
765
3/6
✓ Branch 3 → 4 taken 201 times.
✗ Branch 3 → 22 not taken.
✓ Branch 4 → 5 taken 201 times.
✗ Branch 4 → 20 not taken.
✓ Branch 5 → 6 taken 201 times.
✗ Branch 5 → 20 not taken.
201 cacheStats << FunctionManager::dumpLookupCacheStatistics() << std::endl;
766
3/6
✓ Branch 7 → 8 taken 201 times.
✗ Branch 7 → 25 not taken.
✓ Branch 8 → 9 taken 201 times.
✗ Branch 8 → 23 not taken.
✓ Branch 9 → 10 taken 201 times.
✗ Branch 9 → 23 not taken.
201 cacheStats << StructManager::dumpLookupCacheStatistics() << std::endl;
767
3/6
✓ Branch 11 → 12 taken 201 times.
✗ Branch 11 → 28 not taken.
✓ Branch 12 → 13 taken 201 times.
✗ Branch 12 → 26 not taken.
✓ Branch 13 → 14 taken 201 times.
✗ Branch 13 → 26 not taken.
201 cacheStats << InterfaceManager::dumpLookupCacheStatistics() << std::endl;
768
1/2
✓ Branch 15 → 16 taken 201 times.
✗ Branch 15 → 29 not taken.
201 compilerOutput.cacheStats = cacheStats.str();
769 201 }
770
771 void SourceFile::dumpCompilationStats() const {
772 const size_t sourceFileCount = resourceManager.sourceFiles.size();
773 const size_t totalLineCount = resourceManager.getTotalLineCount();
774 const size_t totalTypeCount = TypeRegistry::getTypeCount();
775 const size_t allocatedBytes = resourceManager.astNodeAlloc.getTotalAllocatedSize();
776 const size_t allocationCount = resourceManager.astNodeAlloc.getAllocationCount();
777 const size_t totalDuration = resourceManager.totalTimer.getDurationMilliseconds();
778 std::cout << "\nSuccessfully compiled " << std::to_string(sourceFileCount) << " source file(s)";
779 std::cout << " or " << std::to_string(totalLineCount) << " lines in total.\n";
780 std::cout << "Total number of blocks allocated via BlockAllocator: " << CommonUtil::formatBytes(allocatedBytes);
781 std::cout << " in " << std::to_string(allocationCount) << " allocations.\n";
782 #ifndef NDEBUG
783 resourceManager.astNodeAlloc.printAllocatedClassStatistic();
784 #endif
785 std::cout << "Total number of types: " << std::to_string(totalTypeCount) << "\n";
786 std::cout << "Total compile time: " << std::to_string(totalDuration) << " ms\n";
787 }
788
789 void SourceFile::dumpOutput(const std::string &content, const std::string &caption, const std::string &fileSuffix) const {
790 if (cliOptions.dump.dumpToFiles) {
791 // Dump to file
792 const std::string dumpFileName = filePath.stem().string() + "-" + fileSuffix;
793 std::filesystem::path dumpFilePath = cliOptions.outputDir / dumpFileName;
794 dumpFilePath.make_preferred();
795 FileUtil::writeToFile(dumpFilePath, content);
796 } else {
797 // Dump to console
798 std::cout << "\n" << caption << ":\n" << content;
799 }
800
801 // If the abort after dump is requested, set the abort compilation flag
802 if (cliOptions.dump.abortAfterDump) {
803 // If this is an IR dump whilst having optimization enabled, we may not abort when dumping unoptimized IR,
804 // because we also have to dump the optimized IR
805 if (cliOptions.dump.dumpIR && fileSuffix == "ir-code.ll") {
806 resourceManager.abortCompilation = cliOptions.optLevel == OptLevel::O0;
807 } else {
808 resourceManager.abortCompilation = true;
809 }
810 }
811 }
812
813 1819 void SourceFile::visualizerPreamble(std::stringstream &output) const {
814
2/2
✓ Branch 2 → 3 taken 271 times.
✓ Branch 2 → 4 taken 1548 times.
1819 if (isMainFile)
815 271 output << "digraph {\n rankdir=\"TB\";\n";
816 else
817 1548 output << "subgraph {\n";
818
3/6
✓ Branch 6 → 7 taken 1819 times.
✗ Branch 6 → 13 not taken.
✓ Branch 7 → 8 taken 1819 times.
✗ Branch 7 → 11 not taken.
✓ Branch 8 → 9 taken 1819 times.
✗ Branch 8 → 11 not taken.
1819 output << " label=\"" << filePath.generic_string() << "\";\n";
819 1819 }
820
821 void SourceFile::visualizerOutput(std::string outputName, const std::string &output) const {
822 if (cliOptions.dump.dumpToFiles) {
823 // Check if graphviz is installed
824 // GCOV_EXCL_START
825 if (!SystemUtil::isGraphvizInstalled())
826 throw CompilerError(IO_ERROR, "Please check if you have installed Graphviz and added it to the PATH variable");
827 // GCOV_EXCL_STOP
828
829 // Write to a dot file
830 std::ranges::transform(outputName, outputName.begin(), ::tolower);
831 dumpOutput(output, outputName, outputName + ".dot");
832
833 // Generate SVG. This only works if the dot code was dumped into a file
834 std::cout << "\nGenerating SVG file ... ";
835 const std::string dotFileName = filePath.stem().string() + "-" + outputName + ".dot";
836 std::filesystem::path dotFilePath = cliOptions.outputDir / dotFileName;
837 std::filesystem::path svgFilePath = dotFilePath;
838 svgFilePath.replace_extension("svg");
839 dotFilePath.make_preferred();
840 svgFilePath.make_preferred();
841 SystemUtil::exec("dot -T svg -o" + svgFilePath.string() + " " + dotFilePath.string());
842 std::cout << "done.\nSVG file can be found at: " << svgFilePath << "\n";
843 } else {
844 // Dump to console
845 std::cout << "\nSerialized " << outputName << ":\n\n" << output << "\n";
846 }
847
848 // If the abort after dump is requested, set the abort compilation flag
849 if (cliOptions.dump.abortAfterDump)
850 resourceManager.abortCompilation = true;
851 }
852
853 13956 void SourceFile::printStatusMessage(const char *stage, const CompileStageIOType &in, const CompileStageIOType &out,
854 uint64_t stageRuntime, unsigned short stageRuns) const {
855
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 30 taken 13956 times.
13956 if (cliOptions.printDebugOutput) {
856 static constexpr const char *const compilerStageIoTypeName[6] = {"Code", "Tokens", "CST", "AST", "IR", "Obj"};
857 // Build output string
858 std::stringstream outputStr;
859 outputStr << "[" << stage << "] for " << fileName << ": ";
860 outputStr << compilerStageIoTypeName[in] << " --> " << compilerStageIoTypeName[out];
861 outputStr << " (" << std::to_string(stageRuntime) << " ms";
862 if (stageRuns > 0)
863 outputStr << "; " << std::to_string(stageRuns) << " run(s)";
864 outputStr << ")\n";
865 // Print
866 std::cout << outputStr.str();
867 }
868 13956 }
869
870 } // namespace spice::compiler
871