diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..89dead5 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,73 @@ +Checks: '*, + -abseil-*, + -altera-*, + -android-*, + -boost-*, + -darwin-*, + -fuchsia-*, + -google-*, + -hicpp-*, + -linuxkernel-*, + -llvm-*, + -llvmlibc-*, + -mpi-*, + -objc-*, + -zircon-*, + + -bugprone-easily-swappable-parameters, + -bugprone-exception-escape, + -bugprone-unhandled-exception-at-new, + -cert-dcl21-cpp, + -cert-err58-cpp, + -cppcoreguidelines-avoid-capturing-lambda-coroutines, + -cppcoreguidelines-avoid-const-or-ref-data-members, + -cppcoreguidelines-avoid-do-while, + -cppcoreguidelines-avoid-reference-coroutine-parameters, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-non-private-member-variables-in-classes, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-bounds-constant-array-index, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-type-member-init, + -cppcoreguidelines-pro-type-reinterpret-cast, + -cppcoreguidelines-pro-type-static-cast-downcast, + -cppcoreguidelines-pro-type-union-access, + -cppcoreguidelines-pro-type-vararg, + -cppcoreguidelines-special-member-functions, + -modernize-macro-to-enum, + -misc-include-cleaner, + -misc-no-recursion, + -misc-non-private-member-variables-in-classes, + -misc-use-anonymous-namespace, + -modernize-return-braced-init-list, + -modernize-use-auto, + -modernize-use-trailing-return-type, + -portability-simd-intrinsics, + -readability-avoid-unconditional-preprocessor-if, + -readability-container-data-pointer, + -readability-convert-member-functions-to-static, + -readability-implicit-bool-conversion, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-named-parameter, + -readability-redundant-access-specifiers, + -readability-uppercase-literal-suffix, + -readability-use-anyofallof' + +CheckOptions: + - key: readability-identifier-length.IgnoredParameterNames + value: '^[xyz]$' + - key: readability-identifier-length.IgnoredLoopCounterNames + value: '^[xyz]$' + - key: readability-identifier-length.IgnoredVariableNames + value: '(it|NO)' + - key: readability-function-cognitive-complexity.Threshold + value: 50 + +WarningsAsErrors: '*' + +HeaderFilterRegex: 'source/*.hpp$' + +UseColor: false diff --git a/source/mijin/container/boxed_object.hpp b/source/mijin/container/boxed_object.hpp index c131991..c4a9d26 100644 --- a/source/mijin/container/boxed_object.hpp +++ b/source/mijin/container/boxed_object.hpp @@ -53,7 +53,7 @@ public: #if MIJIN_BOXED_OBJECT_DEBUG ~BoxedObject() { - MIJIN_ASSERT(!constructed, "BoxedObject::~BoxedObject(): Object has not been destroyed prior to destructor!") + MIJIN_ASSERT(!constructed, "BoxedObject::~BoxedObject(): Object has not been destroyed prior to destructor!"); } #endif @@ -69,7 +69,7 @@ public: void construct(TArgs&&... args) { #if MIJIN_BOXED_OBJECT_DEBUG - MIJIN_ASSERT(!constructed, "BoxedObject::construct(): Attempt to construct an already constructed object!") + MIJIN_ASSERT(!constructed, "BoxedObject::construct(): Attempt to construct an already constructed object!"); constructed = true; #endif std::construct_at(&object_, std::forward(args)...); @@ -78,7 +78,7 @@ public: void destroy() { #if MIJIN_BOXED_OBJECT_DEBUG - MIJIN_ASSERT(constructed, "BoxedObject::destroy(): Attempt to destroy a not constructed object!") + MIJIN_ASSERT(constructed, "BoxedObject::destroy(): Attempt to destroy a not constructed object!"); constructed = false; #endif std::destroy_at(&object_); @@ -87,7 +87,7 @@ public: void copyTo(BoxedObject& other) const { #if MIJIN_BOXED_OBJECT_DEBUG - MIJIN_ASSERT(constructed, "BoxedObject::copy(): Attempt to copy a not constructed object!") + MIJIN_ASSERT(constructed, "BoxedObject::copy(): Attempt to copy a not constructed object!"); #endif other.construct(object_); } @@ -95,7 +95,7 @@ public: void moveTo(BoxedObject& other) { #if MIJIN_BOXED_OBJECT_DEBUG - MIJIN_ASSERT(constructed, "BoxedObject::copy(): Attempt to copy a not constructed object!") + MIJIN_ASSERT(constructed, "BoxedObject::copy(): Attempt to copy a not constructed object!"); #endif other.construct(std::move(object_)); destroy(); @@ -104,7 +104,7 @@ public: [[nodiscard]] T& get() { #if MIJIN_BOXED_OBJECT_DEBUG - MIJIN_ASSERT(constructed, "BoxedObject::get(): Attempt to access a not constructed object!") + MIJIN_ASSERT(constructed, "BoxedObject::get(): Attempt to access a not constructed object!"); #endif return object_; } @@ -112,7 +112,7 @@ public: [[nodiscard]] const T& get() const { #if MIJIN_BOXED_OBJECT_DEBUG - MIJIN_ASSERT(constructed, "BoxedObject::get(): Attempt to access a not constructed object!") + MIJIN_ASSERT(constructed, "BoxedObject::get(): Attempt to access a not constructed object!"); #endif return object_; } diff --git a/source/mijin/debug/assert.hpp b/source/mijin/debug/assert.hpp index 915caae..8ae501f 100644 --- a/source/mijin/debug/assert.hpp +++ b/source/mijin/debug/assert.hpp @@ -58,30 +58,33 @@ MIJIN_ERROR(msg) \ std::abort() // TODO: make ignoreAll work (static variables cannot be used in constexpr functions) -#define MIJIN_ASSERT(condition, msg) \ -if (!static_cast(condition)) \ -{ \ - /* static bool ignoreAll = false; */ \ - if (true) /*!ignoreAll */ \ - { \ - const mijin::AssertionResult assertion_result__ = mijin::handleAssert( \ - #condition, msg, std::source_location::current()); \ - switch (assertion_result__) \ - { \ - case mijin::AssertionResult::ABORT: \ - std::abort(); \ - break; \ - case mijin::AssertionResult::IGNORE: \ - /*break*/; \ - case mijin::AssertionResult::IGNORE_ALL: \ - /* ignoreAll = true; */ \ - break; \ - default: /* ERROR */ \ - MIJIN_ERROR("Debug assertion failed: " #condition "\nMessage: " msg); \ - break; \ - } \ - } \ -} +#define MIJIN_ASSERT(condition, msg) \ +do \ +{ \ + if (!static_cast(condition)) \ + { \ + /* static bool ignoreAll = false; */ \ + if (true) /*!ignoreAll */ \ + { \ + const mijin::AssertionResult assertion_result__ = mijin::handleAssert( \ + #condition, msg, std::source_location::current()); \ + switch (assertion_result__) \ + { \ + case mijin::AssertionResult::ABORT: \ + std::abort(); \ + break; \ + case mijin::AssertionResult::IGNORE: \ + /*break*/; \ + case mijin::AssertionResult::IGNORE_ALL: \ + /* ignoreAll = true; */ \ + break; \ + default: /* ERROR */ \ + MIJIN_ERROR("Debug assertion failed: " #condition "\nMessage: " msg); \ + break; \ + } \ + } \ + } \ +} while(false) #define MIJIN_ASSERT_FATAL(condition, msg) \ if (!static_cast(condition)) \ diff --git a/source/mijin/io/stream.cpp b/source/mijin/io/stream.cpp index 2d9ea34..acb1e8b 100644 --- a/source/mijin/io/stream.cpp +++ b/source/mijin/io/stream.cpp @@ -3,7 +3,6 @@ #include #include -#include #include namespace mijin @@ -327,25 +326,25 @@ StreamError FileStream::open(const char* path, FileOpenMode mode_) } int result = std::fseek(handle, 0, SEEK_END); - assert(result == 0); + MIJIN_ASSERT(result == 0, "fseek failed."); length = std::ftell(handle); result = std::fseek(handle, 0, SEEK_SET); - assert(result == 0); + MIJIN_ASSERT(result == 0, "fseek failed."); return StreamError::SUCCESS; } void FileStream::close() { - assert(handle); + MIJIN_ASSERT(handle != nullptr, "FileStream is not open."); const int result = std::fclose(handle); // NOLINT(cppcoreguidelines-owning-memory) - assert(result == 0); + MIJIN_ASSERT(result == 0, "fclose failed."); } StreamError FileStream::readRaw(std::span buffer, const ReadOptions& options, std::size_t* outBytesRead) { - assert(handle); - assert(mode == FileOpenMode::READ || mode == FileOpenMode::READ_WRITE); + MIJIN_ASSERT(handle != nullptr, "FileStream is not open."); + MIJIN_ASSERT(mode == FileOpenMode::READ || mode == FileOpenMode::READ_WRITE, "Cannot read from this stream"); const std::size_t readBytes = std::fread(buffer.data(), 1, buffer.size(), handle); if (std::ferror(handle)) { @@ -369,8 +368,9 @@ StreamError FileStream::readRaw(std::span buffer, const ReadOption StreamError FileStream::writeRaw(std::span buffer) { - assert(handle); - assert(mode == FileOpenMode::WRITE || mode == FileOpenMode::APPEND || mode == FileOpenMode::READ_WRITE); + MIJIN_ASSERT(handle != nullptr, "FileStream is not open."); + MIJIN_ASSERT(mode == FileOpenMode::WRITE || mode == FileOpenMode::READ_WRITE || mode == FileOpenMode::APPEND, + "Cannot write to this stream"); const std::size_t written = std::fwrite(buffer.data(), 1, buffer.size(), handle); if (written != buffer.size() || std::ferror(handle)) { @@ -383,15 +383,13 @@ StreamError FileStream::writeRaw(std::span buffer) std::size_t FileStream::tell() { - assert(handle); - + MIJIN_ASSERT(handle != nullptr, "FileStream is not open."); return std::ftell(handle); } StreamError FileStream::seek(std::intptr_t pos, SeekMode seekMode) { - assert(handle); - + MIJIN_ASSERT(handle != nullptr, "FileStream is not open."); int origin; // NOLINT(cppcoreguidelines-init-variables) switch (seekMode) { @@ -405,7 +403,7 @@ StreamError FileStream::seek(std::intptr_t pos, SeekMode seekMode) origin = SEEK_END; break; default: - assert(!"Invalid value passed as seekMode!"); + MIJIN_ERROR("Invalid value passed as seekMode!"); return StreamError::UNKNOWN_ERROR; } const int result = std::fseek(handle, static_cast(pos), origin); @@ -418,19 +416,19 @@ StreamError FileStream::seek(std::intptr_t pos, SeekMode seekMode) void FileStream::flush() { const int result = std::fflush(handle); - assert(result == 0); + MIJIN_ASSERT(result == 0, "fflush failed."); } bool FileStream::isAtEnd() { - assert(handle); + MIJIN_ASSERT(handle != nullptr, "FileStream is not open."); (void) std::fgetc(handle); if (std::feof(handle)) { return true; } const int result = std::fseek(handle, -1, SEEK_CUR); - assert(result == 0); + MIJIN_ASSERT(result == 0, "fseek failed."); return false; } @@ -454,7 +452,7 @@ StreamFeatures FileStream::getFeatures() void MemoryStream::openRW(std::span data) { - assert(!isOpen()); + MIJIN_ASSERT(!isOpen(), "MemoryStream is already open."); data_ = data; pos_ = 0; canWrite_ = true; @@ -462,7 +460,7 @@ void MemoryStream::openRW(std::span data) void MemoryStream::openROImpl(const void* data, std::size_t bytes) { - assert(!isOpen()); + MIJIN_ASSERT(!isOpen(), "MemoryStream is already open."); data_ = std::span(const_cast(static_cast(data)), bytes); // NOLINT(cppcoreguidelines-pro-type-const-cast) we'll be fine pos_ = 0; canWrite_ = false; @@ -470,13 +468,13 @@ void MemoryStream::openROImpl(const void* data, std::size_t bytes) void MemoryStream::close() { - assert(isOpen()); + MIJIN_ASSERT(isOpen(), "MemoryStream is not open."); data_ = {}; } StreamError MemoryStream::readRaw(std::span buffer, const ReadOptions& options, std::size_t* outBytesRead) { - assert(isOpen()); + MIJIN_ASSERT(isOpen(), "MemoryStream is not open."); if (!options.partial && availableBytes() < buffer.size()) { return StreamError::IO_ERROR; // TODO: need more errors? } @@ -493,8 +491,11 @@ StreamError MemoryStream::readRaw(std::span buffer, const ReadOpti StreamError MemoryStream::writeRaw(std::span buffer) { - assert(isOpen()); - assert(canWrite_); + MIJIN_ASSERT(isOpen(), "MemoryStream is not open."); + + if (!canWrite_) { + return StreamError::NOT_SUPPORTED; + } if (availableBytes() < buffer.size()) { return StreamError::IO_ERROR; @@ -506,13 +507,13 @@ StreamError MemoryStream::writeRaw(std::span buffer) std::size_t MemoryStream::tell() { - assert(isOpen()); + MIJIN_ASSERT(isOpen(), "MemoryStream is not open."); return pos_; } StreamError MemoryStream::seek(std::intptr_t pos, SeekMode seekMode) { - assert(isOpen()); + MIJIN_ASSERT(isOpen(), "MemoryStream is not open."); std::intptr_t newPos = -1; switch (seekMode) { @@ -535,7 +536,7 @@ StreamError MemoryStream::seek(std::intptr_t pos, SeekMode seekMode) bool MemoryStream::isAtEnd() { - assert(isOpen()); + MIJIN_ASSERT(isOpen(), "MemoryStream is not open."); return pos_ == data_.size(); } diff --git a/source/mijin/io/stream.hpp b/source/mijin/io/stream.hpp index bc9615c..4defaa2 100644 --- a/source/mijin/io/stream.hpp +++ b/source/mijin/io/stream.hpp @@ -4,7 +4,6 @@ #if !defined(MIJIN_IO_STREAM_HPP_INCLUDED) #define MIJIN_IO_STREAM_HPP_INCLUDED 1 -#include #include #include #include @@ -322,8 +321,9 @@ public: } void close(); [[nodiscard]] inline bool isOpen() const { return data_.data() != nullptr; } - [[nodiscard]] inline std::size_t availableBytes() const { - assert(isOpen()); + [[nodiscard]] inline std::size_t availableBytes() const + { + MIJIN_ASSERT(isOpen(), "MemoryStream is not open."); return data_.size() - pos_; } diff --git a/source/mijin/util/bitarray.hpp b/source/mijin/util/bitarray.hpp index 9e17728..f4a6f62 100644 --- a/source/mijin/util/bitarray.hpp +++ b/source/mijin/util/bitarray.hpp @@ -6,7 +6,6 @@ #include #include -#include #include namespace mijin @@ -32,8 +31,9 @@ private: using byte_type = std::conditional_t; std::array bytes; public: - [[nodiscard]] bool get(std::size_t index) const { - assert(index < numBits); + [[nodiscard]] bool get(std::size_t index) const + { + MIJIN_ASSERT(index < numBits, "BitArray: index out of range."); return (bytes[index / 8] & (1 << (index % 8))); } void set(std::size_t index, bool value) diff --git a/source/mijin/util/os.cpp b/source/mijin/util/os.cpp index 21ef754..ea787b3 100644 --- a/source/mijin/util/os.cpp +++ b/source/mijin/util/os.cpp @@ -51,13 +51,13 @@ Result openSharedLibrary(std::string_view libraryFile) noexcept { #if MIJIN_TARGET_OS == MIJIN_OS_LINUX const std::unique_lock dlErrorLock(gDlErrorMutex); - dlerror(); + dlerror(); // NOLINT(concurrency-mt-unsafe) we take care of that const fs::path libraryPath = fs::absolute(libraryFile); void* ptr = dlopen(libraryPath.c_str(), RTLD_NOW); if (ptr == nullptr) { - return ResultError(dlerror()); + return ResultError(dlerror()); // NOLINT(concurrency-mt-unsafe) we take care of that } return LibraryHandle{.data = dlopen(libraryPath.c_str(), RTLD_NOW)}; #elif MIJIN_TARGET_OS == MIJIN_OS_WINDOWS