GCC Code Coverage Report


Directory: ../
File: src/SourceFile.cpp
Date: 2025-03-05 01:50:32
Exec Total Coverage
Lines: 446 523 85.3%
Functions: 39 41 95.1%
Branches: 469 1024 45.8%

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