Skip to content

Commit

Permalink
Fix compiler warnings and add tests for them (#216)
Browse files Browse the repository at this point in the history
* Add warning for generic types with single type condition

* Add more tests for warnings

* Separate lexer and parser errors

* Add testing mechanism for warnings

* Fix minor issues and update test refs

* Fix Windows paths in warnings
  • Loading branch information
marcauberer authored Oct 12, 2022
1 parent ec3eb03 commit 105a941
Show file tree
Hide file tree
Showing 148 changed files with 399 additions and 223 deletions.
Empty file added .fleet/settings.json
Empty file.
1 change: 1 addition & 0 deletions .run/spice.run.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="spice" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O2 -ir ../../media/test-project/os-test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<envs>
<env name="CONSOLE_WIDTH" value="300" />
<env name="RUN_TESTS" value="OFF" />
<env name="SPICE_STD_DIR" value="$PROJECT_DIR$/std" />
</envs>
Expand Down
14 changes: 9 additions & 5 deletions media/test-project/os-test.spice
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import "std/type/int" as i;

f<int> main() {
int v1 = 10;
int v2 = v1;
printf("v1: %d, v2: %d\n", v1, v2);
v2++;
printf("v1: %d, v2: %d\n", v1, v2);
bool a = false;
bool b = true;
if a = b {
printf("a: %d, b: %d", a, b);
} else {
printf("a: %d, b: %d", a, b);
}
}

/*import "std/net/http" as http;
Expand Down
6 changes: 4 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ set(SOURCES
exception/IRError.h
exception/LinkerError.cpp
exception/LinkerError.h
exception/LexerParserError.cpp
exception/LexerParserError.h
exception/LexerError.cpp
exception/LexerError.h
exception/ParserError.cpp
exception/ParserError.h
exception/AntlrThrowingErrorListener.cpp
exception/AntlrThrowingErrorListener.h
visualizer/CSTVisualizerVisitor.cpp
Expand Down
29 changes: 17 additions & 12 deletions src/analyzer/AnalyzerVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ std::any AnalyzerVisitor::visitEntry(EntryNode *node) {
if (requiresMainFct && !hasMainFunction)
throw SemanticError(node, MISSING_MAIN_FUNCTION, "No main function found");

// Print compiler warnings once the whole ast is present, but not for std files
if (requiresMainFct && !isStdFile && !reAnalyze)
rootScope->printCompilerWarnings();
// Append the scope warnings to the other warnings in the source file
if (!reAnalyze && runNumber > 1)
for (const CompilerWarning &warning : rootScope->collectWarnings())
sourceFile.compilerOutput.warnings.push_back(warning);

// Increment run number if the source file gets analyzed again
runNumber++;
Expand Down Expand Up @@ -663,6 +664,11 @@ std::any AnalyzerVisitor::visitGenericTypeDef(GenericTypeDefNode *node) {
typeConditions.push_back(typeCondition);
}

// Check if only one type condition is set
if (typeConditions.size() == 1 && !typeConditions.at(0).is(TY_DYN))
sourceFile.compilerOutput.warnings.emplace_back(node->typeAltsLst()->codeLoc, SINGLE_GENERIC_TYPE_CONDITION,
"Generic type is locked to one type");

// Build symbol specifiers
GenericType genericType = GenericType(node->typeName, typeConditions);
auto structSymbolSpecifiers = SymbolSpecifiers(genericType);
Expand Down Expand Up @@ -943,10 +949,16 @@ std::any AnalyzerVisitor::visitIfStmt(IfStmtNode *node) {
currentScope = currentScope->createChildBlock(node->getScopeId(), SCOPE_IF_BODY);

// Visit condition
auto conditionType = any_cast<SymbolType>(visit(node->condition()));
AssignExprNode *condition = node->condition();
auto conditionType = any_cast<SymbolType>(visit(condition));
if (!conditionType.is(TY_BOOL))
throw SemanticError(node->condition(), CONDITION_MUST_BE_BOOL, "If condition must be of type bool");

// Warning for bool assignment
if (condition->hasOperator && condition->op == AssignExprNode::OP_ASSIGN)
sourceFile.compilerOutput.warnings.emplace_back(condition->codeLoc, BOOL_ASSIGN_AS_CONDITION,
"If you want to compare the values, use '=='");

// Visit statement list in new scope
visit(node->stmtLst());

Expand Down Expand Up @@ -1308,13 +1320,6 @@ std::any AnalyzerVisitor::visitAssignExpr(AssignExprNode *node) {
Capture *lhsCapture = currentScope->lookupCapture(variableName);
if (lhsCapture)
lhsCapture->setCaptureMode(READ_WRITE);

// Print compiler warning if the rhs size exceeds the lhs size
if (lhsTy.isArray() && rhsTy.getArraySize() > lhsTy.getArraySize())
CompilerWarning(node->rhs()->codeLoc, ARRAY_TOO_MANY_VALUES,
"You provided more values "
"than your array can hold. Excess variables are being ignored by the compiler.")
.print();
}

return node->setEvaluatedSymbolType(rhsTy);
Expand Down Expand Up @@ -2387,7 +2392,7 @@ SymbolType AnalyzerVisitor::initExtStruct(SymbolTable *sourceScope, const std::s

// Mount the external struct table to the new position in the root scope of the current source file
rootScope->mountChildBlock(STRUCT_SCOPE_PREFIX + newStructSignature, externalStructTable, false);
rootScope->lookupTable(STRUCT_SCOPE_PREFIX + newStructSignature)->areCompilerWarningsEnabled = false;
rootScope->lookupTable(STRUCT_SCOPE_PREFIX + newStructSignature)->isShadowTable = true;

return newStructTy;
}
Expand Down
17 changes: 14 additions & 3 deletions src/dependency/SourceFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,12 @@ void SourceFile::analyze() {
void SourceFile::reAnalyze() {
// Re-Analyze this source file
bool repetitionRequired;
unsigned int analyzeCount = 0;
unsigned int reAnalyzeCount = 0;
do {
repetitionRequired = any_cast<bool>(analyzer->visit(ast.get()));
antlrCtx.parser->reset();
analyzeCount++;
if (analyzeCount >= 10)
reAnalyzeCount++;
if (reAnalyzeCount >= 10)
throw std::runtime_error("Internal compiler error: Number of analyzer runs for one source file exceeded. Please report "
"this as a bug on GitHub.");
} while (repetitionRequired);
Expand Down Expand Up @@ -317,6 +317,17 @@ bool SourceFile::isAlreadyImported(const std::string &filePathSearch) const {
return parent != nullptr && parent->isAlreadyImported(filePathSearch);
}

void SourceFile::printWarnings() const {
// Print warnings for all dependencies
for (const auto &dependency : dependencies) {
if (!dependency.second.first->stdFile)
dependency.second.first->printWarnings();
}
// Print warnings for this file
for (const CompilerWarning &warning : compilerOutput.warnings)
warning.print();
}

void SourceFile::requestRuntimeModule(const RuntimeModuleName &moduleName) {
runtimeModuleManager.requestModule(this, moduleName);
}
Expand Down
3 changes: 3 additions & 0 deletions src/dependency/SourceFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <Token.h>

#include <dependency/RuntimeModuleManager.h>
#include <util/CompilerWarning.h>

#include <llvm/IR/IRBuilder.h>

Expand Down Expand Up @@ -41,6 +42,7 @@ struct CompilerOutput {
std::string symbolTableString;
std::string irString;
std::string irOptString;
std::vector<CompilerWarning> warnings;
};

class SourceFile {
Expand All @@ -67,6 +69,7 @@ class SourceFile {
void addDependency(const std::shared_ptr<SourceFile> &sourceFile, const AstNode *declAstNode, const std::string &name,
const std::string &filePath);
[[nodiscard]] bool isAlreadyImported(const std::string &filePathSearch) const;
void printWarnings() const;
void requestRuntimeModule(const RuntimeModuleName &moduleName);
[[nodiscard]] SymbolTable *getRuntimeModuleScope(const RuntimeModuleName &moduleName) const;

Expand Down
8 changes: 6 additions & 2 deletions src/exception/AntlrThrowingErrorListener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

#include "AntlrThrowingErrorListener.h"

#include <exception/LexerParserError.h>
#include <exception/LexerError.h>
#include <exception/ParserError.h>
#include <util/CodeLoc.h>

void AntlrThrowingErrorListener::syntaxError(antlr4::Recognizer *recognizer, antlr4::Token *offendingSymbol, size_t line,
size_t charPositionInLine, const std::string &msg, std::exception_ptr e) {
throw LexerParserError(CodeLoc(line, charPositionInLine), mode == LEXER ? TOKENIZING_FAILED : PARSING_FAILED, msg);
if (mode == LEXER)
throw LexerError(CodeLoc(line, charPositionInLine), TOKENIZING_FAILED, msg);
else
throw ParserError(CodeLoc(line, charPositionInLine), PARSING_FAILED, msg);
}
3 changes: 1 addition & 2 deletions src/exception/CliError.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
#include "CliError.h"

/**
* @param token Syntax token, where the error occurred
* @param type Type of the error
* @param message Error message suffix
*/
CliError::CliError(const CliErrorType &type, const std::string &message) {
errorMessage = "CLI error: " + getMessagePrefix(type) + ": " + message;
errorMessage = "[Error|CLI] " + getMessagePrefix(type) + ": " + message;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/exception/IRError.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* @param message Error message suffix
*/
IRError::IRError(const CodeLoc &codeLoc, const IRErrorType &type, const std::string &message) {
errorMessage = "Internal compiler error in " + codeLoc.toPrettyString() + ": " + getMessagePrefix(type) + ": " + message;
errorMessage = "[Error|IR] " + codeLoc.toPrettyString() + ": " + getMessagePrefix(type) + ": " + message;
}

/**
Expand All @@ -22,7 +22,7 @@ IRError::IRError(const CodeLoc &codeLoc, const IRErrorType &type, const std::str
* @param message Error message suffix
*/
IRError::IRError(const IRErrorType &type, const std::string &message) {
errorMessage = "Internal compiler: " + getMessagePrefix(type) + ": " + message;
errorMessage = "[Error|IR] " + getMessagePrefix(type) + ": " + message;
}

/**
Expand Down
37 changes: 37 additions & 0 deletions src/exception/LexerError.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) 2021-2022 ChilliBits. All rights reserved.

#include "LexerError.h"

#include <util/CodeLoc.h>

/**
* Constructor: Used in case that the exact code position where the error occurred is known
*
* @param codeLoc Code location where the error occurred
* @param type Type of the error
* @param message Error message suffix
*/
LexerError::LexerError(const CodeLoc &codeLoc, const LexerErrorType &type, const std::string &message) {
errorMessage = "[Error|Lexer] " + codeLoc.toPrettyString() + ": " + getMessagePrefix(type) + ": " + message;
}

/**
* Get the message for this particular error instance
*
* @return Error message in form of a char array
*/
const char *LexerError::what() const noexcept { return errorMessage.c_str(); }

/**
* Get the prefix of the error message for a particular error
*
* @param type Type of the error
* @return Prefix string for the error type
*/
std::string LexerError::getMessagePrefix(LexerErrorType type) {
switch (type) {
case TOKENIZING_FAILED:
return "Tokenizing failed";
}
return "Unknown error"; // GCOV_EXCL_LINE
}
31 changes: 31 additions & 0 deletions src/exception/LexerError.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2021-2022 ChilliBits. All rights reserved.

#pragma once

#include <exception>
#include <string>
#include <utility>

// Forward declarations
struct CodeLoc;

enum LexerErrorType { TOKENIZING_FAILED };

/**
* Custom exception for errors, occurring while lexing
*/
class LexerError : public std::exception {
public:
// Constructors
explicit LexerError(const CodeLoc &codeLoc, const LexerErrorType &type, const std::string &message);

// Public methods
[[nodiscard]] const char *what() const noexcept override;

private:
// Members
std::string errorMessage;

// Private methods
[[nodiscard]] static std::string getMessagePrefix(LexerErrorType errorType);
};
31 changes: 0 additions & 31 deletions src/exception/LexerParserError.h

This file was deleted.

3 changes: 1 addition & 2 deletions src/exception/LinkerError.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
#include "LinkerError.h"

/**
* @param token Syntax token, where the error occurred
* @param type Type of the error
* @param message Error message suffix
*/
LinkerError::LinkerError(const LinkerErrorType &type, const std::string &message) {
errorMessage = "Linker error: " + getMessagePrefix(type) + ": " + message;
errorMessage = "[Error|Linker] " + getMessagePrefix(type) + ": " + message;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) 2021-2022 ChilliBits. All rights reserved.

#include "LexerParserError.h"
#include "ParserError.h"

#include <util/CodeLoc.h>

Expand All @@ -11,27 +11,25 @@
* @param type Type of the error
* @param message Error message suffix
*/
LexerParserError::LexerParserError(const CodeLoc &codeLoc, const LexerParserErrorType &type, const std::string &message) {
errorMessage = "Error in " + codeLoc.toPrettyString() + ": " + getMessagePrefix(type) + ": " + message;
ParserError::ParserError(const CodeLoc &codeLoc, const ParserErrorType &type, const std::string &message) {
errorMessage = "[Error|Parser] " + codeLoc.toPrettyString() + ": " + getMessagePrefix(type) + ": " + message;
}

/**
* Get the message for this particular error instance
*
* @return Error message in form of a char array
*/
const char *LexerParserError::what() const noexcept { return errorMessage.c_str(); }
const char *ParserError::what() const noexcept { return errorMessage.c_str(); }

/**
* Get the prefix of the error message for a particular error
*
* @param type Type of the error
* @return Prefix string for the error type
*/
std::string LexerParserError::getMessagePrefix(LexerParserErrorType type) {
std::string ParserError::getMessagePrefix(ParserErrorType type) {
switch (type) {
case TOKENIZING_FAILED:
return "Tokenizing failed";
case PARSING_FAILED:
return "Parsing failed";
case NUMBER_OUT_OF_RANGE:
Expand Down
Loading

0 comments on commit 105a941

Please sign in to comment.