• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rieske / trans / 29861581689

21 Jul 2026 07:27PM UTC coverage: 91.142% (+0.4%) from 90.735%
29861581689

push

github

web-flow
Merge pull request #52 from rieske/tests/fuzz

Fuzz test and fix issues

86 of 110 new or added lines in 9 files covered. (78.18%)

5371 of 5893 relevant lines covered (91.14%)

368978.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.28
/src/semantic_analyzer/SemanticAnalysisVisitor.cpp
1
#include "SemanticAnalysisVisitor.h"
2

3
#include <algorithm>
4
#include <cctype>
5
#include <stdexcept>
6
#include <string>
7

8
#include "translation_unit/Context.h"
9
#include "types/Type.h"
10
#include "util/Logger.h"
11
#include "util/LogManager.h"
12

13
namespace semantic_analyzer {
14

15
static const translation_unit::Context EXTERNAL_CONTEXT {"external", 0};
16

17
static Logger& err = LogManager::getErrorLogger();
18

19
// Locals are stored as `$s<scopeId><name>` in the symbol table; strip that prefix for diagnostics
20
// and for looking up file-scope functions under their source names.
21
static std::string unscopedSymbolName(const std::string& name) {
946✔
22
    if (name.size() > 2 && name[0] == '$' && name[1] == 's') {
946✔
23
        std::size_t i = 2;
4✔
24
        while (i < name.size() && std::isdigit(static_cast<unsigned char>(name[i]))) {
8✔
25
            ++i;
4✔
26
        }
27
        if (i > 2 && i < name.size()) {
4✔
28
            return name.substr(i);
4✔
29
        }
30
    }
31
    return name;
942✔
32
}
33

34
SemanticAnalysisVisitor::SemanticAnalysisVisitor() {
418✔
35
    type::Type functionType = type::function(type::signedInteger());
418✔
36
    symbolTable.insertFunction("printf", functionType.getFunction(), EXTERNAL_CONTEXT);
1,254✔
37
    symbolTable.insertFunction("scanf", functionType.getFunction(), EXTERNAL_CONTEXT);
1,254✔
38
}
418✔
39

40
SemanticAnalysisVisitor::~SemanticAnalysisVisitor() {
418✔
41
}
418✔
42

43
void SemanticAnalysisVisitor::printSymbolTable() const {
392✔
44
    symbolTable.printTable();
392✔
45
}
392✔
46

47
void SemanticAnalysisVisitor::visit(ast::DeclarationSpecifiers& declarationSpecifiers) {
1,222✔
48
    // FIXME: this would look so much better
49
    /*for (std::string error : declarationSpecifiers.getSemanticErrors()) {
50
     semanticError(error, globalContext);
51
     }*/
52
    if (declarationSpecifiers.getStorageSpecifiers().size() > 1) {
1,222✔
53
        semanticError("multiple storage classes in declaration specifiers",
×
54
                declarationSpecifiers.getStorageSpecifiers().at(1).getContext());
×
55
    }
56
}
1,222✔
57

58
void SemanticAnalysisVisitor::visit(ast::Declaration& declaration) {
520✔
59
    declaration.visitChildren(*this);
520✔
60

61
    auto baseType = declaration.getDeclarationSpecifiers().getTypeSpecifiers().at(0).getType();
520✔
62
    for (const auto& declarator : declaration.getDeclarators()) {
1,162✔
63
        auto type = declarator->getFundamentalType(baseType);
642✔
64
        if (type.isVoid()) {
642✔
65
            semanticError("variable `" + declarator->getName() + "` declared void", declarator->getContext());
2✔
66
        } else if (symbolTable.isAtFileScope() && symbolTable.hasFunction(declarator->getName())) {
640✔
67
            semanticError("symbol `" + declarator->getName() + "` declaration conflicts with function of the same name",
×
68
                    declarator->getContext());
×
69
        } else if (symbolTable.insertSymbol(declarator->getName(), type, declarator->getContext())) {
640✔
70
            declarator->setHolder(symbolTable.lookup(declarator->getName()));
638✔
71
            // TODO: type check initializers
72
            if (declarator->hasInitializer() && symbolTable.isAtFileScope()) {
638✔
73
                long initValue = 0;
28✔
74
                if (declarator->getInitializer()->evaluateConstant(initValue)) {
28✔
75
                    symbolTable.setGlobalInitializer(declarator->getName(), initValue);
28✔
76
                } else {
77
                    semanticError("global initializer is not a constant expression", declarator->getContext());
×
78
                }
79
            }
80
        } else {
81
            semanticError(
2✔
82
                    "symbol `" + declarator->getName() +
4✔
83
                            "` declaration conflicts with previous declaration on " +
4✔
84
                            to_string(symbolTable.lookup(declarator->getName()).getContext()),
4✔
85
                    declarator->getContext());
4✔
86
        }
87
    }
642✔
88
}
520✔
89

90
void SemanticAnalysisVisitor::visit(ast::Declarator& declarator) {
1,346✔
91
    declarator.visitChildren(*this);
1,346✔
92
}
1,346✔
93

94
void SemanticAnalysisVisitor::visit(ast::InitializedDeclarator& declarator) {
642✔
95
    declarator.visitChildren(*this);
642✔
96
}
642✔
97

98
void SemanticAnalysisVisitor::visit(ast::ArrayAccess& arrayAccess) {
2✔
99
    arrayAccess.visitLeftOperand(*this);
2✔
100
    arrayAccess.visitRightOperand(*this);
2✔
101

102
    // Operand failed to resolve (e.g. undeclared identifier) — error already reported.
103
    if (!arrayAccess.hasLeftOperandSymbol() || !arrayAccess.hasRightOperandSymbol()) {
2✔
104
        return;
2✔
105
    }
106

107
    auto type = arrayAccess.leftOperandType();
×
108
    if (type.isPointer()) {
×
109
        arrayAccess.setLvalue(symbolTable.createTemporarySymbol(type.dereference()));
×
110
        arrayAccess.setResultSymbol(symbolTable.createTemporarySymbol(type.dereference()));
×
111
    } else {
112
        semanticError("invalid type for operator[]\n", arrayAccess.getContext());
×
113
    }
114
}
×
115

116
void SemanticAnalysisVisitor::visit(ast::FunctionCall& functionCall) {
950✔
117
    functionCall.visitOperand(*this);
950✔
118
    functionCall.visitArguments(*this);
950✔
119

120
    // Operand failed to resolve (e.g. undeclared identifier) — error already reported.
121
    if (!functionCall.hasOperandSymbol()) {
950✔
122
        return;
8✔
123
    }
124

125
    // ValueEntry names for locals are scope-prefixed (e.g. `$s1a`); functions are stored under
126
    // the source identifier. A mangled name is always a local (or temp), never a function entry —
127
    // do not look up the demangled form, or a local would incorrectly call a same-named function.
128
    const auto symbolName = functionCall.operandSymbol()->getName();
946✔
129
    const auto displayName = unscopedSymbolName(symbolName);
946✔
130
    if (symbolName != displayName || !symbolTable.hasFunction(displayName)) {
946✔
131
        semanticError("called object `" + displayName + "` is not a function", functionCall.getContext());
4✔
132
        return;
4✔
133
    }
134

135
    auto functionSymbol = symbolTable.findFunction(displayName);
942✔
136

137
    functionCall.setSymbol(functionSymbol);
942✔
138

139
    auto& arguments = functionCall.getArgumentList();
942✔
140
    if (arguments.size() == functionSymbol.argumentCount()) {
942✔
141
        auto declaredArguments = functionSymbol.arguments();
118✔
142
        for (std::size_t i { 0 }; i < arguments.size(); ++i) {
320✔
143
            if (!arguments.at(i)->hasResultSymbol()) {
202✔
NEW
144
                return;
×
145
            }
146
            const auto& declaredArgument = declaredArguments.at(i);
202✔
147
            const auto& actualArgument = arguments.at(i)->getResultSymbol();
202✔
148
            typeCheck(actualArgument->getType(), declaredArgument, functionCall.getContext());
202✔
149
        }
150

151
        auto returnType = functionSymbol.returnType();
118✔
152
        if (!returnType.isVoid()) {
118✔
153
            functionCall.setResultSymbol(symbolTable.createTemporarySymbol(returnType));
118✔
154
        }
155
    } else if (functionSymbol.getContext() == EXTERNAL_CONTEXT) {
942✔
156
    // FIXME: using EXTERNAL_CONTEXT as a workaround for printf/scanf external functions until varargs are properly implemented
157
        auto returnType = functionSymbol.returnType();
824✔
158
        if (!returnType.isVoid()) {
824✔
159
            functionCall.setResultSymbol(symbolTable.createTemporarySymbol(returnType));
824✔
160
        }
161
    } else {
824✔
162
        semanticError("no match for function " + functionSymbol.getType().to_string(), functionCall.getContext());
×
163
    }
164
}
950✔
165

166
void SemanticAnalysisVisitor::visit(ast::IdentifierExpression& identifier) {
2,798✔
167
    if (symbolTable.hasSymbol(identifier.getIdentifier())) {
2,798✔
168
        identifier.setResultSymbol(symbolTable.lookup(identifier.getIdentifier()));
2,786✔
169
    } else {
170
        semanticError("symbol `" + identifier.getIdentifier() + "` is not defined", identifier.getContext());
12✔
171
    }
172
}
2,798✔
173

174
void SemanticAnalysisVisitor::visit(ast::ConstantExpression& constant) {
1,356✔
175
    constant.setResultSymbol(symbolTable.createTemporarySymbol(constant.getType()));
1,356✔
176
}
1,356✔
177

178
void SemanticAnalysisVisitor::visit(ast::StringLiteralExpression& stringLiteral) {
828✔
179
    std::string constantSymbol = symbolTable.newConstant(stringLiteral.getValue());
828✔
180
    stringLiteral.setConstantSymbol(constantSymbol);
828✔
181
    stringLiteral.setResultSymbol(symbolTable.createTemporarySymbol(stringLiteral.getType()));
828✔
182
}
828✔
183

184
void SemanticAnalysisVisitor::visit(ast::PostfixExpression& expression) {
26✔
185
    expression.visitOperand(*this);
26✔
186
    if (!expression.hasOperandSymbol()) {
26✔
NEW
187
        return;
×
188
    }
189

190
    expression.setType(expression.operandType());
26✔
191
    auto operandSymbol = *expression.operandSymbol();
26✔
192
    expression.setResultSymbol(operandSymbol);
26✔
193

194
    auto preOperationSymbolName = operandSymbol.getName() + "_pre";
26✔
195
    symbolTable.insertSymbol(preOperationSymbolName, operandSymbol.getType(), operandSymbol.getContext());
26✔
196
    expression.setPreOperationSymbol(symbolTable.lookup(preOperationSymbolName));
26✔
197

198
    if (!expression.isLval()) {
26✔
199
        semanticError("lvalue required as increment operand", expression.getContext());
×
200
    }
201
}
26✔
202

203
void SemanticAnalysisVisitor::visit(ast::PrefixExpression& expression) {
28✔
204
    expression.visitOperand(*this);
28✔
205
    if (!expression.hasOperandSymbol()) {
28✔
NEW
206
        return;
×
207
    }
208

209
    expression.setType(expression.operandType());
28✔
210
    expression.setResultSymbol(*expression.operandSymbol());
28✔
211

212
    if (!expression.isLval()) {
28✔
213
        semanticError("lvalue required as increment operand", expression.getContext());
×
214
    }
215
}
216

217
void SemanticAnalysisVisitor::visit(ast::UnaryExpression& expression) {
546✔
218
    expression.visitOperand(*this);
546✔
219
    if (!expression.hasOperandSymbol()) {
546✔
220
        return;
4✔
221
    }
222

223
    switch (expression.getOperator()->getLexeme().front()) {
542✔
224
    case '&':
328✔
225
        expression.setResultSymbol(symbolTable.createTemporarySymbol(type::pointer(expression.operandType())));
328✔
226
        break;
328✔
227
    case '*':
92✔
228
        if (expression.operandType().isPointer()) {
92✔
229
            expression.setResultSymbol(symbolTable.createTemporarySymbol(expression.operandType().dereference()));
92✔
230
            expression.setLvalueSymbol(symbolTable.createTemporarySymbol(expression.operandType()));
92✔
231
        } else {
232
            semanticError("invalid type argument of ‘unary *’ :" + expression.operandType().to_string(), expression.getContext());
×
233
        }
234
        break;
92✔
235
    case '+':
4✔
236
        expression.setResultSymbol(*expression.operandSymbol());
4✔
237
        break;
4✔
238
    case '-':
88✔
239
        expression.setResultSymbol(symbolTable.createTemporarySymbol(expression.operandType()));
88✔
240
        break;
88✔
241
    case '!':
30✔
242
        expression.setResultSymbol(symbolTable.createTemporarySymbol(type::signedInteger()));
30✔
243
        expression.setTruthyLabel(symbolTable.newLabel());
30✔
244
        expression.setFalsyLabel(symbolTable.newLabel());
30✔
245
        break;
30✔
246
    default:
×
247
        throw std::runtime_error { "Unidentified increment operator: " + expression.getOperator()->getLexeme() };
×
248
    }
249
}
250

251
void SemanticAnalysisVisitor::visit(ast::TypeCast& expression) {
×
252
    expression.visitOperand(*this);
×
NEW
253
    if (!expression.hasOperandSymbol()) {
×
NEW
254
        return;
×
255
    }
256

257
    expression.setResultSymbol(symbolTable.createTemporarySymbol(expression.getType().getType()));
×
258
}
259

260
void SemanticAnalysisVisitor::visit(ast::ArithmeticExpression& expression) {
120✔
261
    expression.visitLeftOperand(*this);
120✔
262
    expression.visitRightOperand(*this);
120✔
263
    if (!expression.hasLeftOperandSymbol() || !expression.hasRightOperandSymbol()) {
120✔
NEW
264
        return;
×
265
    }
266

267
    typeCheck(
120✔
268
            expression.leftOperandType(),
240✔
269
            expression.rightOperandType(),
240✔
270
            expression.getContext());
240✔
271
    // FIXME: type conversion
272
    expression.setResultSymbol(symbolTable.createTemporarySymbol(expression.leftOperandType()));
120✔
273
}
274

275
void SemanticAnalysisVisitor::visit(ast::ShiftExpression& expression) {
20✔
276
    expression.visitLeftOperand(*this);
20✔
277
    expression.visitRightOperand(*this);
20✔
278
    if (!expression.hasLeftOperandSymbol() || !expression.hasRightOperandSymbol()) {
20✔
NEW
279
        return;
×
280
    }
281

282
    if (expression.rightOperandType().isPrimitive() && !expression.rightOperandType().getPrimitive().isFloating()) {
20✔
283
        expression.setResultSymbol(symbolTable.createTemporarySymbol(expression.leftOperandType()));
20✔
284
    } else {
285
        semanticError("argument of type int required for shift expression", expression.getContext());
×
286
    }
287
}
288

289
void SemanticAnalysisVisitor::visit(ast::ComparisonExpression& expression) {
186✔
290
    expression.visitLeftOperand(*this);
186✔
291
    expression.visitRightOperand(*this);
186✔
292
    if (!expression.hasLeftOperandSymbol() || !expression.hasRightOperandSymbol()) {
186✔
NEW
293
        return;
×
294
    }
295

296
    typeCheck(
186✔
297
            expression.leftOperandType(),
372✔
298
            expression.rightOperandType(),
372✔
299
            expression.getContext());
372✔
300

301
    expression.setResultSymbol(symbolTable.createTemporarySymbol(type::signedInteger()));
186✔
302
    expression.setTruthyLabel(symbolTable.newLabel());
186✔
303
    expression.setFalsyLabel(symbolTable.newLabel());
186✔
304
}
305

306
void SemanticAnalysisVisitor::visit(ast::BitwiseExpression& expression) {
16✔
307
    expression.visitLeftOperand(*this);
16✔
308
    expression.visitRightOperand(*this);
16✔
309
    if (!expression.hasLeftOperandSymbol() || !expression.hasRightOperandSymbol()) {
16✔
NEW
310
        return;
×
311
    }
312
    expression.setType(expression.leftOperandType());
16✔
313

314
    typeCheck(
16✔
315
            expression.leftOperandType(),
32✔
316
            expression.rightOperandType(),
32✔
317
            expression.getContext());
32✔
318

319
    expression.setResultSymbol(
16✔
320
            symbolTable.createTemporarySymbol(expression.getType()));
32✔
321
}
322

323
void SemanticAnalysisVisitor::visit(ast::LogicalAndExpression& expression) {
12✔
324
    expression.visitLeftOperand(*this);
12✔
325
    expression.visitRightOperand(*this);
12✔
326
    if (!expression.hasLeftOperandSymbol() || !expression.hasRightOperandSymbol()) {
12✔
NEW
327
        return;
×
328
    }
329

330
    typeCheck(
12✔
331
            expression.leftOperandType(),
24✔
332
            expression.rightOperandType(),
24✔
333
            expression.getContext());
24✔
334

335
    expression.setResultSymbol(symbolTable.createTemporarySymbol(type::signedInteger()));
12✔
336
    expression.setExitLabel(symbolTable.newLabel());
12✔
337
}
338

339
void SemanticAnalysisVisitor::visit(ast::LogicalOrExpression& expression) {
12✔
340
    expression.visitLeftOperand(*this);
12✔
341
    expression.visitRightOperand(*this);
12✔
342
    if (!expression.hasLeftOperandSymbol() || !expression.hasRightOperandSymbol()) {
12✔
NEW
343
        return;
×
344
    }
345

346
    typeCheck(
12✔
347
            expression.leftOperandType(),
24✔
348
            expression.rightOperandType(),
24✔
349
            expression.getContext());
24✔
350

351
    expression.setResultSymbol(symbolTable.createTemporarySymbol(type::signedInteger()));
12✔
352
    expression.setExitLabel(symbolTable.newLabel());
12✔
353
}
354

355
void SemanticAnalysisVisitor::visit(ast::AssignmentExpression& expression) {
494✔
356
    expression.visitLeftOperand(*this);
494✔
357
    expression.visitRightOperand(*this);
494✔
358
    if (!expression.hasLeftOperandSymbol() || !expression.hasRightOperandSymbol()) {
494✔
359
        return;
4✔
360
    }
361

362
    if (expression.isLval()) {
490✔
363
        typeCheck(
486✔
364
                expression.leftOperandType(),
972✔
365
                expression.rightOperandType(),
972✔
366
                expression.getContext());
972✔
367

368
        expression.setResultSymbol(*expression.leftOperandSymbol());
486✔
369
    } else {
370
        semanticError("lvalue required on the left side of assignment", expression.getContext());
12✔
371
    }
372
}
373

374
void SemanticAnalysisVisitor::visit(ast::ExpressionList& expression) {
2✔
375
    expression.visitLeftOperand(*this);
2✔
376
    expression.visitRightOperand(*this);
2✔
377
    if (!expression.hasRightOperandSymbol()) {
2✔
NEW
378
        return;
×
379
    }
380
    // Comma operator: value and type of the right operand
381
    expression.setType(expression.rightOperandType());
2✔
382
    expression.setResultSymbol(*expression.rightOperandSymbol());
2✔
383
}
384

385
void SemanticAnalysisVisitor::visit(ast::Operator&) {
×
386
}
×
387

388
void SemanticAnalysisVisitor::visit(ast::JumpStatement& statement) {
×
389
    throw std::runtime_error { "not implemented" };
×
390
}
391

392
void SemanticAnalysisVisitor::visit(ast::ReturnStatement& statement) {
502✔
393
    statement.returnExpression->accept(*this);
502✔
394
}
502✔
395

396
void SemanticAnalysisVisitor::visit(ast::VoidReturnStatement& statement) {
22✔
397
}
22✔
398

399
void SemanticAnalysisVisitor::visit(ast::IfStatement& statement) {
26✔
400
    statement.testExpression->accept(*this);
26✔
401
    statement.body->accept(*this);
26✔
402

403
    statement.setFalsyLabel(symbolTable.newLabel());
26✔
404
}
26✔
405

406
void SemanticAnalysisVisitor::visit(ast::IfElseStatement& statement) {
18✔
407
    statement.testExpression->accept(*this);
18✔
408
    statement.truthyBody->accept(*this);
18✔
409
    statement.falsyBody->accept(*this);
18✔
410

411
    statement.setFalsyLabel(symbolTable.newLabel());
18✔
412
    statement.setExitLabel(symbolTable.newLabel());
18✔
413
}
18✔
414

415
void SemanticAnalysisVisitor::visit(ast::LoopStatement& loop) {
58✔
416
    loop.header->accept(*this);
58✔
417
    loop.body->accept(*this);
58✔
418
}
58✔
419

420
void SemanticAnalysisVisitor::visit(ast::ForLoopHeader& loopHeader) {
36✔
421
    if (loopHeader.initialization) {
36✔
422
        loopHeader.initialization->accept(*this);
22✔
423
    }
424
    if (loopHeader.clause) {
36✔
425
        loopHeader.clause->accept(*this);
26✔
426
    }
427
    if (loopHeader.increment) {
36✔
428
        loopHeader.increment->accept(*this);
24✔
429
    }
430

431
    loopHeader.setLoopEntry(symbolTable.newLabel());
36✔
432
    loopHeader.setLoopExit(symbolTable.newLabel());
36✔
433
}
36✔
434

435
void SemanticAnalysisVisitor::visit(ast::WhileLoopHeader& loopHeader) {
22✔
436
    loopHeader.clause->accept(*this);
22✔
437

438
    loopHeader.setLoopEntry(symbolTable.newLabel());
22✔
439
    loopHeader.setLoopExit(symbolTable.newLabel());
22✔
440
}
22✔
441

442
void SemanticAnalysisVisitor::visit(ast::Pointer&) {
70✔
443
}
70✔
444

445
void SemanticAnalysisVisitor::visit(ast::Identifier&) {
816✔
446
}
816✔
447

448
void SemanticAnalysisVisitor::visit(ast::ArrayDeclarator& declaration) {
×
449
    declaration.subscriptExpression->accept(*this);
×
450
    throw std::runtime_error { "not implemented" };
×
451
}
452

453
void SemanticAnalysisVisitor::visit(ast::FunctionDeclarator& declarator) {
522✔
454
    declarator.visitFormalArguments(*this);
522✔
455

456
    argumentNames.clear();
522✔
457
    std::vector<type::Type> arguments;
522✔
458
    for (auto& argumentDeclaration : declarator.getFormalArguments()) {
704✔
459
        arguments.push_back(argumentDeclaration.getType());
182✔
460
        argumentNames.push_back(argumentDeclaration.getName());
182✔
461
    }
462

463
    // FIXME: return type is not known at this point!
464
    type::Type functionType = type::function(type::signedInteger(), arguments);
522✔
465
    if (symbolTable.hasGlobalVariable(declarator.getName())) {
522✔
466
        semanticError("function `" + declarator.getName() + "` conflicts with global variable of the same name",
2✔
467
                declarator.getContext());
4✔
468
        return;
2✔
469
    }
470
    FunctionEntry functionEntry = symbolTable.insertFunction(
471
            declarator.getName(),
1,040✔
472
            functionType.getFunction(),
1,040✔
473
            declarator.getContext());
1,560✔
474

475
    if (functionEntry.getContext() != declarator.getContext()) {
520✔
476
        semanticError("function `" + declarator.getName() + "` definition conflicts with previous one on "
×
477
                + to_string(functionEntry.getContext()), declarator.getContext());
×
478
    }
479
}
524✔
480

481
void SemanticAnalysisVisitor::visit(ast::FormalArgument& argument) {
182✔
482
    argument.visitSpecifiers(*this);
182✔
483
    argument.visitDeclarator(*this);
182✔
484
    if (argument.getType().isVoid()) {
182✔
485
        semanticError("function argument ‘" + argument.getName() + "’ declared void", argument.getDeclarationContext());
×
486
    }
487
}
182✔
488

489
void SemanticAnalysisVisitor::visit(ast::FunctionDefinition& function) {
520✔
490
    function.visitReturnType(*this);
520✔
491
    function.visitDeclarator(*this);
520✔
492

493
    if (!symbolTable.hasFunction(function.getName())) {
520✔
494
        return;
2✔
495
    }
496
    function.setSymbol(symbolTable.findFunction(function.getName()));
518✔
497
    symbolTable.startFunction(function.getName(), argumentNames);
518✔
498
    // Parameters and outermost body declarations share one scope (C); do not enterBlockScope.
499
    function.visitBodyChildren(*this);
518✔
500
    function.setArguments(symbolTable.getCurrentScopeArguments());
518✔
501
    function.setLocalVariables(symbolTable.getCurrentScopeSymbols());
518✔
502
    symbolTable.endFunction();
518✔
503
}
504

505
void SemanticAnalysisVisitor::visit(ast::Block& block) {
130✔
506
    symbolTable.enterBlockScope();
130✔
507
    block.visitChildren(*this);
130✔
508
    symbolTable.exitBlockScope();
130✔
509
}
130✔
510

511
void SemanticAnalysisVisitor::typeCheck(const type::Type& typeFrom, const type::Type& typeTo,
1,034✔
512
        const translation_unit::Context& context)
513
{
514
    if (!typeTo.canAssignFrom(typeFrom)) {
1,034✔
515
        semanticError("type mismatch: can't convert " + typeFrom.to_string() + " to " + typeTo.to_string(), context);
×
516
    }
517
}
1,034✔
518

519
void SemanticAnalysisVisitor::semanticError(std::string message, const translation_unit::Context& context) {
26✔
520
    containsSemanticErrors = true;
26✔
521
    err << context << ": error: " << message << "\n";
26✔
522
}
26✔
523

524
bool SemanticAnalysisVisitor::successfulSemanticAnalysis() const {
418✔
525
    return !containsSemanticErrors;
418✔
526
}
527

528
std::map<std::string, std::string> SemanticAnalysisVisitor::getConstants() const {
392✔
529
    return symbolTable.getConstants();
392✔
530
}
531

532
std::vector<ValueEntry> SemanticAnalysisVisitor::getGlobalVariables() const {
392✔
533
    return symbolTable.getGlobalVariables();
392✔
534
}
535

536
} // namespace semantic_analyzer
537

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc