Skip to content

Conversation

@kimkulling
Copy link
Owner

@kimkulling kimkulling commented Nov 18, 2025

Summary by CodeRabbit

  • New Features

    • Command-line argument parser with validation and help output.
    • String tokenization utility.
    • New renderer pipeline type exported for future use.
  • Bug Fixes

    • More robust application shutdown with null-pointer guard.
  • Improvements

    • Decorative framed formatting for startup/shutdown logs.
    • Renderer initialization sequence adjusted.
  • Chores

    • Updated third-party submodule pointer.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 18, 2025

Walkthrough

Adds ArgumentParser and Tokenizer core utilities and registers them in the runtime build; introduces a StringArray alias; small app shutdown/logging adjustments; adds RenderPipelineState type; updates cppcore submodule pointer; removes a swapchain creation call from Vulkan init.

Changes

Cohort / File(s) Summary
Build Configuration
src/runtime/CMakeLists.txt
Added core/argumentparser.h, core/argumentparser.cpp, core/tokenizer.cpp, core/tokenizer.h to segfault_core_src. Verify correct inclusion in targets and export/install behavior.
Application Lifecycle
src/runtime/application/app.cpp
Reworked start/end log banners; added releaseSDL() and replaced direct SDL_Quit() calls; App::shutdown() now checks mSdlWindow for null and early-returns. Confirm shutdown ordering and resource release.
Core — ArgumentParser
src/runtime/core/argumentparser.h, src/runtime/core/argumentparser.cpp
New segfault::core::ArgumentParser with parsing, validation, help generation, iteration, and error reporting. Copy operations deleted. Review parsing rules, parameter-count syntax, and error handling.
Core — Tokenizer
src/runtime/core/tokenizer.h, src/runtime/core/tokenizer.cpp
New segfault::core::Tokenizer with static and instance tokenization, internal token storage, and behavior to skip empty/leading-space tokens. Validate delimiter handling and edge cases.
Core Infrastructure
src/runtime/core/segfault.h
Added #include <cppcore/Container/TArray.h>, conditional _CRT_SECURE_NO_WARNINGS, and using StringArray = cppcore::TArray<std::string>;. Confirm include ordering and alias scope.
Renderer Types
src/runtime/renderer/rendercore.h
Added empty segfault::renderer::RenderPipelineState struct; DrawCommand contains a commented reference. Check for intended future use and ABI impact.
Renderer Init Sequence
src/runtime/renderer/RHIVulkan.cpp
Removed mImpl->createSwapChain() call from RHI::init after device creation. Review where swapchain creation now occurs and ensure correct Vulkan init ordering.
Submodule
src/contrib/cppcore
Updated submodule pointer only (no code diffs in this PR). Ensure the updated submodule commit is intentional and builds cleanly.

Sequence Diagram(s)

(No sequence diagram generated — changes do not introduce a new multi-component control flow requiring visualization.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

  • Feature/add uniform buffers #3 — touches src/runtime/application/app.cpp, modifying shutdown logging and sequence; likely related to the same shutdown changes in this PR.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.77% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Add argument parser' accurately reflects the main change: introducing a new ArgumentParser class with supporting tokenizer and build configuration updates.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/argument_parser

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0e8c40 and d03026f.

📒 Files selected for processing (1)
  • src/runtime/core/tokenizer.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/runtime/core/tokenizer.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build (ubuntu-latest, Release, gcc)
  • GitHub Check: build (ubuntu-latest, Release, clang)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/runtime/renderer/RHIVulkan.cpp (1)

334-369: Logical device queue creation uses only one queue create info

The switch to cppcore::TArray is fine, but the VkDeviceCreateInfo wiring is now inconsistent:

  • You build queueCreateInfos for all unique queue families.
  • Then you set createInfo.queueCreateInfoCount from queueCreateInfos.size() and createInfo.pQueueCreateInfos = queueCreateInfos.data();
  • Finally, createInfo.queueCreateInfoCount is overwritten to 1, so only the first queue create info is actually used, even if there are two unique families (graphics + present).
  • The separate queueCreateInfo / queuePrioritys block is now redundant and confusing.

This can result in missing queues on devices where graphics and present families differ.

A minimal fix is to drop the redundant local queueCreateInfo block and consistently use queueCreateInfos:

-        VkDeviceQueueCreateInfo queueCreateInfo{};
-        queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
-        queueCreateInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
-        queueCreateInfo.queueCount = 1;
-
-        // priorities
-        float queuePrioritys[2] = { 1.f, 1.f };
-        
-        queueCreateInfo.pNext = nullptr;        
-        queueCreateInfo.pQueuePriorities = &queuePrioritys[0];
-
         VkPhysicalDeviceFeatures deviceFeatures{};
         VkDeviceCreateInfo createInfo{};
         createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
-        
-        createInfo.pNext = nullptr;
-        createInfo.pQueueCreateInfos = &queueCreateInfo;
-
-        createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
-        createInfo.pQueueCreateInfos = queueCreateInfos.data();
-
-        createInfo.queueCreateInfoCount = 1;
+        
+        createInfo.pNext = nullptr;
+        createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
+        createInfo.pQueueCreateInfos = queueCreateInfos.data();

This makes the advertised queues match the uniqueQueueFamilies you collected.

🧹 Nitpick comments (3)
src/runtime/application/app.cpp (1)

124-135: Early return in App::shutdown can skip parts of shutdown

The new guard

if (mSdlWindow == nullptr) {
    logMessage(LogType::Error, "Invalid application state, cannot shutdown.");
    return;
}

avoids destroying a null window, but it also prevents the rest of the shutdown path (e.g., SDL_Quit() and any future teardown you add here) from running if mSdlWindow is unexpectedly null.

If App::shutdown is intended to be robust/idempotent, consider:

  • Logging the error but still calling SDL_Quit() (and any RHI teardown) when appropriate, or
  • Narrowing the guard to just skip SDL_DestroyWindow while allowing the rest of shutdown to proceed.
src/runtime/core/argumentparser.cpp (2)

23-33: Include standard headers for C library functions used in this TU

This file uses several C library functions:

  • ::strlen (constructor)
  • memset, sprintf (showHelp)
  • atoi (parseArgParameter)

but only includes project headers. Relying on transitive includes from other headers makes the translation unit fragile.

Make the file self-contained by adding the relevant standard headers:

 #include "core/argumentparser.h"
 #include "core/tokenizer.h"
+
+#include <cstring>  // strlen, memset
+#include <cstdio>   // sprintf
+#include <cstdlib>  // atoi

44-71: Clean up redundant pre-tokenization size check in parseExpectedArgs

In parseExpectedArgs you have:

StringArray extractedArgs, extractedDescs;
if (extractedDescs.size() != extractedArgs.size()) {
    error += "Missing description for arguments";
    return false;
}

Tokenizer::tokenize(userDefinedArgs, extractedArgs, ":");
Tokenizer::tokenize(desc, extractedDescs, ":");
uint32_t numParam(0);
if (extractedArgs.size() != extractedDescs.size()) {
    error += "Number of arguments and descriptions do not match";
    return false;
}

The first size comparison is always false (both arrays are just constructed and empty), so that branch is dead code. The real validation happens after tokenization.

You can simplify this helper by dropping the unused pre-check:

-    StringArray extractedArgs, extractedDescs;
-    if (extractedDescs.size() != extractedArgs.size()) {
-        error += "Missing description for arguments";
-        return false;
-    }
-
-    Tokenizer::tokenize(userDefinedArgs, extractedArgs, ":");
-    Tokenizer::tokenize(desc, extractedDescs, ":");
+    StringArray extractedArgs, extractedDescs;
+    Tokenizer::tokenize(userDefinedArgs, extractedArgs, ":");
+    Tokenizer::tokenize(desc, extractedDescs, ":");

This keeps behaviour unchanged while making the code clearer.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3df0fdc and 8b0ddbc.

📒 Files selected for processing (10)
  • src/runtime/CMakeLists.txt (1 hunks)
  • src/runtime/application/app.cpp (2 hunks)
  • src/runtime/core/argumentparser.cpp (1 hunks)
  • src/runtime/core/argumentparser.h (1 hunks)
  • src/runtime/core/filearchive.h (0 hunks)
  • src/runtime/core/segfault.h (2 hunks)
  • src/runtime/core/tokenizer.cpp (1 hunks)
  • src/runtime/core/tokenizer.h (1 hunks)
  • src/runtime/renderer/RHIVulkan.cpp (2 hunks)
  • src/runtime/renderer/rendercore.h (1 hunks)
💤 Files with no reviewable changes (1)
  • src/runtime/core/filearchive.h
🧰 Additional context used
🧬 Code graph analysis (5)
src/runtime/application/app.cpp (1)
src/runtime/core/segfault.h (1)
  • logMessage (60-75)
src/runtime/core/tokenizer.h (1)
src/runtime/core/tokenizer.cpp (14)
  • getNumOfToken (9-11)
  • getNumOfToken (9-9)
  • getTokenAt (13-19)
  • getTokenAt (13-13)
  • isEmpty (21-23)
  • isEmpty (21-21)
  • clear (25-27)
  • clear (25-25)
  • tokenize (29-50)
  • tokenize (29-29)
  • tokenize (52-60)
  • tokenize (52-52)
  • isLineBreak (62-68)
  • isLineBreak (62-62)
src/runtime/core/segfault.h (1)
src/runtime/core/argumentparser.h (2)
  • string (78-158)
  • string (160-170)
src/runtime/core/argumentparser.cpp (2)
src/runtime/core/tokenizer.cpp (6)
  • tokenize (29-50)
  • tokenize (29-29)
  • tokenize (52-60)
  • tokenize (52-52)
  • clear (25-27)
  • clear (25-25)
src/runtime/core/argumentparser.h (2)
  • string (78-158)
  • string (160-170)
src/runtime/core/argumentparser.h (1)
src/runtime/core/argumentparser.cpp (28)
  • ArgumentParser (73-128)
  • Argument (34-37)
  • Argument (39-42)
  • showHelp (130-157)
  • showHelp (130-130)
  • getNext (159-170)
  • getNext (159-159)
  • clear (172-178)
  • clear (172-172)
  • isSupported (180-190)
  • isSupported (180-180)
  • getNumValues (192-206)
  • getNumValues (192-192)
  • argument (86-86)
  • getArgument (208-221)
  • getArgument (208-208)
  • hasArgument (223-232)
  • hasArgument (223-223)
  • hasValidArgs (234-236)
  • hasValidArgs (234-234)
  • parseArgParameter (238-271)
  • parseArgParameter (238-238)
  • getBlankArgument (273-294)
  • getBlankArgument (273-273)
  • validateArguments (296-319)
  • validateArguments (296-296)
  • setInvalid (321-323)
  • setInvalid (321-321)
🪛 Clang (14.0.6)
src/runtime/core/tokenizer.h

[error] 3-3: 'core/segfault.h' file not found

(clang-diagnostic-error)

src/runtime/core/tokenizer.cpp

[error] 1-1: 'core/tokenizer.h' file not found

(clang-diagnostic-error)

src/runtime/core/argumentparser.cpp

[error] 23-23: 'core/argumentparser.h' file not found

(clang-diagnostic-error)

src/runtime/core/argumentparser.h

[error] 25-25: 'core/segfault.h' file not found

(clang-diagnostic-error)

🔇 Additional comments (3)
src/runtime/renderer/rendercore.h (1)

24-29: Empty RenderPipelineState placeholder is fine

The empty struct is a reasonable hook for future pipeline configuration attached to DrawCommand. No functional issues as-is.

src/runtime/core/segfault.h (1)

5-6: StringArray alias and TArray include look appropriate

Pulling in cppcore::TArray here and exposing using StringArray = cppcore::TArray<std::string>; in segfault::core matches how ArgumentParser and Tokenizer use string arrays and keeps the container choice centralized.

Also applies to: 77-78

src/runtime/CMakeLists.txt (1)

1-11: Core sources correctly wired into segfault_runtime

Including argumentparser and tokenizer (both .h and .cpp) in segfault_core_src cleanly integrates the new core utilities into the runtime library.

Comment on lines +88 to +93
/// @brief Returns next argument and its content.
/// @param arg [in] Next argument.
/// @param value [in] Content of argument.
/// @return true, if another arguments are supported.
bool getNext(std::string &arg, std::string &value );

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Make getNext iterate over detected arguments, not all supported ones

According to the header, getNext should iterate over the arguments actually detected on the command line. In argumentparser.cpp (lines 159‑169), the current implementation is:

bool ArgumentParser::getNext(std::string &arg, std::string &value) {
    if (mSupportedArguments.isEmpty() || mStoredArguments.isEmpty() ||
            mCurrentIndex >= mSupportedArguments.size()) {
        return false;
    }

    arg = mSupportedArguments[mCurrentIndex].mArgument;
    value = mStoredArguments[mCurrentIndex];
    ++mCurrentIndex;

    return true;
}

Issues:

  • Iteration limit is mSupportedArguments.size(), but values are stored only for detected arguments, so indexes can go past mStoredArguments.size().
  • arg comes from mSupportedArguments, while value comes from mStoredArguments; if some supported options were not passed, they will be paired with unrelated values.

Use the detected list as the iteration basis:

-bool ArgumentParser::getNext(std::string &arg, std::string &value) {
-    if (mSupportedArguments.isEmpty() || mStoredArguments.isEmpty() ||
-            mCurrentIndex >= mSupportedArguments.size()) {
+bool ArgumentParser::getNext(std::string &arg, std::string &value) {
+    if (mDetectedArgs.isEmpty() || mStoredArguments.isEmpty() ||
+        mCurrentIndex >= mDetectedArgs.size()) {
         return false;
     }

-    arg = mSupportedArguments[mCurrentIndex].mArgument;
+    arg = mDetectedArgs[mCurrentIndex];
     value = mStoredArguments[mCurrentIndex];
     ++mCurrentIndex;

     return true;
 }

This keeps arg and value aligned and prevents out-of-bounds access on mStoredArguments.

🤖 Prompt for AI Agents
In src/runtime/core/argumentparser.h around lines 88–93: getNext currently
iterates over mSupportedArguments and pairs entries from mSupportedArguments
with values from mStoredArguments, which can cause out-of-bounds access and
misaligned pairs; change getNext to iterate over the detected/stored arguments
list instead (use mStoredArguments.size() as the loop bound) and return both arg
and value from the same detected-entry container (i.e., fetch the argument name
from the detected-arguments list that corresponds to mStoredArguments at the
same index, or store arg/value pairs together), and keep appropriate
empty/bounds checks and increment mCurrentIndex accordingly.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8b0ddbc and 8e3d981.

📒 Files selected for processing (1)
  • CMakeLists.txt (1 hunks)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/runtime/core/argumentparser.h (1)

88-93: Make getNext iterate over detected arguments to avoid misalignment / OOB

The header documents getNext as iterating over “next argument and its content”, but the cpp currently does:

bool ArgumentParser::getNext(std::string &arg, std::string &value) {
    if (mSupportedArguments.isEmpty() || mStoredArguments.isEmpty() ||
            mCurrentIndex >= mSupportedArguments.size()) {
        return false;
    }

    arg = mSupportedArguments[mCurrentIndex].mArgument;
    value = mStoredArguments[mCurrentIndex];
    ++mCurrentIndex;
    return true;
}

This iterates up to mSupportedArguments.size() while values come from mStoredArguments (which only has detected args). If some supported options were not passed, mCurrentIndex can exceed mStoredArguments.size() (OOB) and/or pair the wrong arg with a value.

Suggested fix (in argumentparser.cpp), using the detected list consistently:

-bool ArgumentParser::getNext(std::string &arg, std::string &value) {
-    if (mSupportedArguments.isEmpty() || mStoredArguments.isEmpty() ||
-            mCurrentIndex >= mSupportedArguments.size()) {
+bool ArgumentParser::getNext(std::string &arg, std::string &value) {
+    if (mDetectedArgs.isEmpty() || mStoredArguments.isEmpty() ||
+        mCurrentIndex >= mDetectedArgs.size() ||
+        mCurrentIndex >= mStoredArguments.size()) {
         return false;
     }

-    arg = mSupportedArguments[mCurrentIndex].mArgument;
+    arg = mDetectedArgs[mCurrentIndex];
     value = mStoredArguments[mCurrentIndex];
     ++mCurrentIndex;

     return true;
 }

Minor: the Doxygen on Line 89/90 tags arg and value as [in], but they are really output parameters; switching to [out] (or [in,out] if you ever use them that way) would better reflect the API.

🧹 Nitpick comments (2)
src/runtime/core/argumentparser.h (2)

124-128: Align parseArgParameter parameter naming with the Doxygen comment

The declaration uses uint32_t &numPara but the Doxygen comment refers to numParam, which is likely to trigger Doxygen warnings and makes the docs slightly misleading.

You can either rename the parameter or fix the comment. Renaming for consistency is probably clearer:

-    static bool parseArgParameter( const std::string &arg, uint32_t &numPara );
+    static bool parseArgParameter( const std::string &arg, uint32_t &numParam );

Don’t forget to update the definition in argumentparser.cpp accordingly.


156-158: Consider std::string_view for addError parameter (perf / flexibility)

Sonar suggests replacing const std::string & with std::string_view here. Since you only append and don’t store the reference, string_view would avoid temporary allocations for string literals and allow more flexible callers.

Within this header:

-inline void ArgumentParser::addError(const std::string &error) {
+inline void ArgumentParser::addError(std::string_view error) {
     mLastError += error;
 }

And add the appropriate include near the top of the file:

#include <string>
#include <string_view>

(Assumes the project is built with at least C++17; otherwise, keeping const std::string & is fine.)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e3d981 and a162b82.

📒 Files selected for processing (2)
  • src/runtime/core/argumentparser.h (1 hunks)
  • src/runtime/core/segfault.h (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/runtime/core/segfault.h
🧰 Additional context used
🧬 Code graph analysis (1)
src/runtime/core/argumentparser.h (1)
src/runtime/core/argumentparser.cpp (28)
  • ArgumentParser (73-128)
  • Argument (34-37)
  • Argument (39-42)
  • showHelp (130-157)
  • showHelp (130-130)
  • getNext (159-170)
  • getNext (159-159)
  • clear (172-178)
  • clear (172-172)
  • isSupported (180-190)
  • isSupported (180-180)
  • getNumValues (192-206)
  • getNumValues (192-192)
  • argument (86-86)
  • getArgument (208-221)
  • getArgument (208-208)
  • hasArgument (223-232)
  • hasArgument (223-223)
  • hasValidArgs (234-236)
  • hasValidArgs (234-234)
  • parseArgParameter (238-271)
  • parseArgParameter (238-238)
  • getBlankArgument (273-294)
  • getBlankArgument (273-273)
  • validateArguments (296-319)
  • validateArguments (296-296)
  • setInvalid (321-323)
  • setInvalid (321-321)
🪛 Clang (14.0.6)
src/runtime/core/argumentparser.h

[error] 25-25: 'core/segfault.h' file not found

(clang-diagnostic-error)

🪛 GitHub Check: SonarCloud Code Analysis
src/runtime/core/argumentparser.h

[warning] 156-156: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeALL_X5fEBrIKO-i&open=AZqYeALL_X5fEBrIKO-i&pullRequest=1

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build (windows-latest, Release, cl)
  • GitHub Check: build (ubuntu-latest, Release, clang)
  • GitHub Check: build (ubuntu-latest, Release, gcc)
🔇 Additional comments (1)
src/runtime/core/argumentparser.h (1)

25-25: Verify include path for segfault.h ("core/segfault.h" vs "segfault.h")

Clang in the static analysis report can’t find "core/segfault.h". Please double‑check that your include paths make this header visible from all translation units, or switch to including it relative to this header’s directory if the file really is src/runtime/core/segfault.h.

If it’s in the same directory as this header, one option would be:

-#include "core/segfault.h"
+#include "segfault.h"

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (3)
src/runtime/core/tokenizer.cpp (1)

13-19: Inverted bounds check makes this function always return EmptyToken.

The condition m_Token.size() >= index is incorrect. For any valid index (e.g., index=0 when size=5), this evaluates to true (5 >= 0), causing the function to always return EmptyToken instead of the actual token.

Apply this diff to fix the bounds check:

 const std::string &Tokenizer::getTokenAt( uint32_t index ) const {
-    if ( m_Token.isEmpty() || m_Token.size() >= index ) {
+    if ( m_Token.isEmpty() || index >= m_Token.size() ) {
         return EmptyToken;
     }

     return m_Token[ index ];
 }
src/runtime/core/argumentparser.cpp (2)

69-78: Guard value-checking loop against reading past argc.

The loop iterates from mCurrentIndex + 1 to mCurrentIndex + numValues + 1 without verifying that these indices are within bounds. If numValues is large or there are insufficient arguments remaining, ppArgv[valIdx] will access memory beyond argc, causing undefined behavior.

Apply this diff to add bounds checking:

                     if (const uint32_t numValues = getNumValues(argument)) {
+                        // Ensure sufficient arguments remain
+                        if (mCurrentIndex + numValues >= static_cast<uint32_t>(argc)) {
+                            setInvalid();
+                        } else {
                             // Check the number of expected values for being a new option
-                            for (uint32_t valIdx = mCurrentIndex + 1; valIdx < mCurrentIndex + numValues + 1; valIdx++) {
+                            for (uint32_t valIdx = mCurrentIndex + 1;
+                                 valIdx <= mCurrentIndex + numValues;
+                                 ++valIdx) {
                                 std::string tmpVal(ppArgv[valIdx]);
                                 std::string::size_type pos1 = tmpVal.find(option);
                                 if (pos1 != std::string::npos) {
                                     setInvalid();
                                     break;
                                 }
-                            }
+                            }
+                        }
                     }

137-148: Iterate over detected arguments, not supported arguments.

getNext should iterate over the arguments actually detected on the command line. Currently it uses mSupportedArguments as the iteration basis (line 139, 143) and pairs entries from mSupportedArguments with values from mStoredArguments. If some supported options were not passed, this causes:

  • Out-of-bounds access when mCurrentIndex exceeds mStoredArguments.size()
  • Misaligned argument-value pairs

Apply this diff to use detected arguments:

 bool ArgumentParser::getNext(std::string &arg, std::string &value) {
-    if (mSupportedArguments.isEmpty() || mStoredArguments.isEmpty() ||
-            mCurrentIndex >= mSupportedArguments.size()) {
+    if (mDetectedArgs.isEmpty() || mStoredArguments.isEmpty() ||
+            mCurrentIndex >= mDetectedArgs.size()) {
         return false;
     }
 
-    arg = mSupportedArguments[mCurrentIndex].mArgument;
+    arg = mDetectedArgs[mCurrentIndex];
     value = mStoredArguments[mCurrentIndex];
     ++mCurrentIndex;
 
     return true;
 }
🧹 Nitpick comments (3)
src/runtime/core/tokenizer.cpp (1)

62-68: Verify that Unix-only line break detection is sufficient.

The function only recognizes \n as a line break, ignoring \r (old Mac) and \r\n (Windows). Ensure this behavior aligns with your requirements.

If cross-platform line break support is needed, apply this diff:

 bool Tokenizer::isLineBreak(char c) {
-    if ( c == '\n' ) {
-        return true;
-    } 
-    
-    return false;
+    return (c == '\n' || c == '\r');
 }
src/runtime/core/argumentparser.cpp (2)

126-131: Consider using std::to_string instead of sprintf.

The fixed-size buffer and sprintf are C-style. For simple integer formatting, std::to_string is cleaner and safer.

Apply this diff:

         if (numargs > 0) {
             helpMsg += "\n\tNumber of arguments: ";
-            char buffer[512];
-            memset(buffer, '\0', 512);
-            sprintf(buffer, "%d\n", numargs);
-            helpMsg += std::string(buffer);
+            helpMsg += std::to_string(numargs);
+            helpMsg += "\n";
         }

251-272: Simplify using the position already found.

The code at line 260 finds the position of tokenArgIn but doesn't use it. Instead, lines 266-269 loop again to find the same position. While safe (since existence is verified), this is redundant.

Apply this diff:

 std::string ArgumentParser::getBlankArgument(const std::string &argument) {
-    std::string blankArg("");
-
     // Empty, return it
     if (argument.empty()) {
-        return blankArg;
+        return "";
     }
 
     // Check if any token for parameter definition is there
-    if (std::string::size_type pos = argument.find(tokenArgIn); std::string::npos == pos) {
+    const std::string::size_type pos = argument.find(tokenArgIn);
+    if (std::string::npos == pos) {
         return argument;
     }
 
-    // Clean from parameter-specific token
-    uint32_t i(0);
-    while (argument[i] != tokenArgIn) {
-        blankArg += argument[i];
-        ++i;
-    }
-
-    return blankArg;
+    // Return substring up to the token
+    return argument.substr(0, pos);
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a162b82 and 8a2318b.

📒 Files selected for processing (7)
  • src/runtime/core/argumentparser.cpp (1 hunks)
  • src/runtime/core/argumentparser.h (1 hunks)
  • src/runtime/core/filearchive.h (1 hunks)
  • src/runtime/core/genericfilemanager.h (1 hunks)
  • src/runtime/core/ifilemanager.h (1 hunks)
  • src/runtime/core/tokenizer.cpp (1 hunks)
  • src/runtime/core/tokenizer.h (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/runtime/core/genericfilemanager.h
  • src/runtime/core/ifilemanager.h
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/runtime/core/filearchive.h
🧰 Additional context used
🧬 Code graph analysis (2)
src/runtime/core/tokenizer.h (1)
src/runtime/core/tokenizer.cpp (14)
  • getNumOfToken (9-11)
  • getNumOfToken (9-9)
  • getTokenAt (13-19)
  • getTokenAt (13-13)
  • isEmpty (21-23)
  • isEmpty (21-21)
  • clear (25-27)
  • clear (25-25)
  • tokenize (29-50)
  • tokenize (29-29)
  • tokenize (52-60)
  • tokenize (52-52)
  • isLineBreak (62-68)
  • isLineBreak (62-62)
src/runtime/core/argumentparser.cpp (2)
src/runtime/core/tokenizer.cpp (6)
  • tokenize (29-50)
  • tokenize (29-29)
  • tokenize (52-60)
  • tokenize (52-52)
  • clear (25-27)
  • clear (25-25)
src/runtime/core/argumentparser.h (2)
  • string (56-136)
  • string (138-148)
🪛 Clang (14.0.6)
src/runtime/core/tokenizer.h

[error] 3-3: 'core/segfault.h' file not found

(clang-diagnostic-error)

src/runtime/core/argumentparser.h

[error] 3-3: 'core/segfault.h' file not found

(clang-diagnostic-error)

src/runtime/core/argumentparser.cpp

[error] 1-1: 'core/argumentparser.h' file not found

(clang-diagnostic-error)

src/runtime/core/tokenizer.cpp

[error] 1-1: 'core/tokenizer.h' file not found

(clang-diagnostic-error)

🪛 GitHub Check: SonarCloud Code Analysis
src/runtime/core/argumentparser.h

[warning] 134-134: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeALL_X5fEBrIKO-i&open=AZqYeALL_X5fEBrIKO-i&pullRequest=1

src/runtime/core/argumentparser.cpp

[warning] 25-25: Define each identifier in a dedicated statement.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-o&open=AZqYeAOL_X5fEBrIKO-o&pullRequest=1


[warning] 186-186: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-z&open=AZqYeAOL_X5fEBrIKO-z&pullRequest=1


[warning] 56-56: Use the init-statement to declare "error" inside the if statement.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-p&open=AZqYeAOL_X5fEBrIKO-p&pullRequest=1


[failure] 51-51: Refactor this function to reduce its Cognitive Complexity from 43 to the 25 allowed.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-r&open=AZqYeAOL_X5fEBrIKO-r&pullRequest=1


[warning] 129-129: Use "std::format" or "std::format_to_n" instead of "sprintf". Use "std::format_to" only if buffer size is guaranteed to be sufficient.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-u&open=AZqYeAOL_X5fEBrIKO-u&pullRequest=1


[warning] 223-223: Change this raw for-loop to a range for-loop or an "std::ranges::for_each".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-1&open=AZqYeAOL_X5fEBrIKO-1&pullRequest=1


[failure] 283-283: Refactor this code to not nest more than 3 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-2&open=AZqYeAOL_X5fEBrIKO-2&pullRequest=1


[failure] 68-68: Refactor this code to not nest more than 3 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-s&open=AZqYeAOL_X5fEBrIKO-s&pullRequest=1


[warning] 158-158: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-x&open=AZqYeAOL_X5fEBrIKO-x&pullRequest=1


[warning] 52-52: Do not use the constructor's initializer list for data member "mCurrentIndex". Use the in-class initializer instead.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-m&open=AZqYeAOL_X5fEBrIKO-m&pullRequest=1


[warning] 170-170: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-y&open=AZqYeAOL_X5fEBrIKO-y&pullRequest=1


[warning] 281-281: Change this raw for-loop to a range for-loop or an "std::ranges::for_each".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-3&open=AZqYeAOL_X5fEBrIKO-3&pullRequest=1


[warning] 52-52: Do not use the constructor's initializer list for data member "mIsValid". Use the in-class initializer instead.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-n&open=AZqYeAOL_X5fEBrIKO-n&pullRequest=1


[warning] 201-201: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-0&open=AZqYeAOL_X5fEBrIKO-0&pullRequest=1


[warning] 66-66: Use the init-statement to declare "pos" inside the if statement.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-q&open=AZqYeAOL_X5fEBrIKO-q&pullRequest=1


[warning] 127-127: Use "std::string" instead of a C-style char array.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-v&open=AZqYeAOL_X5fEBrIKO-v&pullRequest=1

src/runtime/core/tokenizer.cpp

[warning] 38-38: Use the init-statement to declare "tmp" inside the if statement.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeANR_X5fEBrIKO-j&open=AZqYeANR_X5fEBrIKO-j&pullRequest=1


[warning] 29-29: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeANR_X5fEBrIKO-l&open=AZqYeANR_X5fEBrIKO-l&pullRequest=1


[warning] 29-29: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeANR_X5fEBrIKO-k&open=AZqYeANR_X5fEBrIKO-k&pullRequest=1

🔇 Additional comments (4)
src/runtime/core/tokenizer.cpp (4)

9-11: LGTM!

The implementation correctly returns the token count.


21-23: LGTM!

The implementation correctly delegates to the underlying container.


25-27: LGTM!

The implementation correctly clears the token container.


52-60: LGTM!

The implementation correctly delegates to the static tokenize method. Note that line 54 uses resize(0) which is functionally equivalent to clear() that's called elsewhere.

Comment on lines +29 to +50
size_t Tokenizer::tokenize(const std::string &str, StringArray &tokens, const std::string &delimiters) {
// Skip delimiters at beginning.
std::string::size_type lastPos = str.find_first_not_of( delimiters, 0 );

// find first "non-delimiter".
std::string::size_type pos = str.find_first_of( delimiters, lastPos );
while( std::string::npos != pos || std::string::npos != lastPos ) {
// Found a token, add it to the vector.
std::string tmp = str.substr( lastPos, pos - lastPos );
if ( !tmp.empty() && ' ' != tmp[ 0 ] ) {
tokens.add( tmp );
}

// Skip delimiters. Note the "not_of"
lastPos = str.find_first_not_of( delimiters, pos );

// Find next "non-delimiter"
pos = str.find_first_of( delimiters, lastPos );
}

return static_cast<uint32_t>( tokens.size() );
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Verify token filtering logic and fix return type inconsistency.

The function has the following concerns:

  1. Line 49: Returns static_cast<uint32_t>(tokens.size()) when the function signature specifies size_t. This narrowing conversion could lose data if the token count exceeds UINT32_MAX.

  2. Line 38: The condition ' ' != tmp[0] filters out tokens starting with a space. Ensure this behavior is intentional for your use case, as general tokenizers typically preserve all tokens between delimiters.

Apply this diff to fix the return type:

-    return static_cast<uint32_t>( tokens.size() );
+    return tokens.size();

Additionally, consider the SonarCloud suggestions:

  • Use std::string_view for the str and delimiters parameters to avoid unnecessary copies.
  • Move the tmp variable declaration into the if statement scope at line 38.
🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[warning] 38-38: Use the init-statement to declare "tmp" inside the if statement.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeANR_X5fEBrIKO-j&open=AZqYeANR_X5fEBrIKO-j&pullRequest=1


[warning] 29-29: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeANR_X5fEBrIKO-l&open=AZqYeANR_X5fEBrIKO-l&pullRequest=1


[warning] 29-29: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeANR_X5fEBrIKO-k&open=AZqYeANR_X5fEBrIKO-k&pullRequest=1

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/runtime/core/argumentparser.cpp (2)

46-94: Fix potential out-of-bounds access when validating option values in the constructor

When an option expects numValues > 0, the for loop over valIdx does not check against argc. If the user provides fewer tokens than numValues, ppArgv[valIdx] is read past argc - 1, which is undefined behaviour and can crash. This is the same issue raised earlier and still present here.

You can guard with an unsigned copy of argc and validate the remaining token count before iterating:

-    if (argc > 1) {
-        while (mCurrentIndex < static_cast<uint32_t>(argc)) {
+    const uint32_t argcU = static_cast<uint32_t>(argc);
+    if (argcU > 1) {
+        while (mCurrentIndex < argcU) {
             std::string argument(ppArgv[mCurrentIndex]);
             const std::string::size_type pos = argument.find(option);
             if (std::string::npos != pos) {
                 argument = argument.substr(pos + optionLen, argument.size() - (pos + optionLen));
                 if (isSupported(argument)) {
                     if (const uint32_t numValues = getNumValues(argument)) {
-                        // Check the number of expected values for being a new option
-                        for (uint32_t valIdx = mCurrentIndex + 1; valIdx < mCurrentIndex + numValues + 1; valIdx++) {
-                            std::string tmpVal(ppArgv[valIdx]);
-                            std::string::size_type pos1 = tmpVal.find(option);
-                            if (pos1 != std::string::npos) {
-                                setInvalid();
-                                break;
-                            }
-                        }
+                        // Ensure there are enough values and none of them is a new option
+                        if (mCurrentIndex + numValues >= argcU) {
+                            setInvalid();
+                        } else {
+                            for (uint32_t valIdx = mCurrentIndex + 1;
+                                 valIdx <= mCurrentIndex + numValues;
+                                 ++valIdx) {
+                                std::string tmpVal(ppArgv[valIdx]);
+                                const std::string::size_type pos1 = tmpVal.find(option);
+                                if (pos1 != std::string::npos) {
+                                    setInvalid();
+                                    break;
+                                }
+                            }
+                        }
                     }

This preserves the original intent (detecting unexpected new options) while avoiding any access beyond ppArgv[argcU - 1].


181-194: Static Dummy string in getArgument is not thread-safe and hides “not found” vs “empty”

getArgument returns a reference to a single static std::string Dummy. Multiple threads calling this concurrently can race on Dummy, and callers cannot distinguish “argument not present” from “present with empty value”. This is the same concern raised previously and still unresolved.

Two options:

  • Minimal change (keep API, fix data race):
 const std::string &ArgumentParser::getArgument(const std::string &arg) const {
-    static std::string Dummy;
+    thread_local std::string Dummy;
  • Better API (if you can change the signature): change the return type to std::optional<std::string> in the header and implementation, return std::nullopt when the arg is empty/not found, and a value copy when found.

Either way, consider documenting the behaviour so callers can reliably distinguish missing arguments.

🧹 Nitpick comments (1)
src/runtime/core/argumentparser.cpp (1)

22-41: parseExpectedArgs silently ignores invalid argument definitions from parseArgParameter

If ArgumentParser::parseArgParameter(arg, numParam) returns false, the current code just skips adding that argument and still returns true with an empty error. That makes misconfigured argument specs fail silently and leaves error unused.

Consider treating a failed parse as a hard error and populating error:

-    for (uint32_t i = 0; i < extractedArgs.size(); ++i) {
-        std::string arg = extractedArgs[i];
-        std::string currentDesc = extractedDescs[i];
-        if (ArgumentParser::parseArgParameter(arg, numParam)) {
-            supportedArgs.add(ArgumentParser::Argument(ArgumentParser::getBlankArgument(arg),
-                    currentDesc, numParam));
-        }
-    }
+    for (uint32_t i = 0; i < extractedArgs.size(); ++i) {
+        std::string arg = extractedArgs[i];
+        std::string currentDesc = extractedDescs[i];
+        if (!ArgumentParser::parseArgParameter(arg, numParam)) {
+            error += "Invalid argument definition: " + arg;
+            return false;
+        }
+
+        supportedArgs.add(ArgumentParser::Argument(
+                ArgumentParser::getBlankArgument(arg),
+                currentDesc,
+                numParam));
+    }

This way configuration mistakes are detected early and reported.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a2318b and 3d4caa9.

📒 Files selected for processing (1)
  • src/runtime/core/argumentparser.cpp (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/runtime/core/argumentparser.cpp (2)
src/runtime/core/tokenizer.cpp (6)
  • tokenize (29-50)
  • tokenize (29-29)
  • tokenize (52-60)
  • tokenize (52-52)
  • clear (25-27)
  • clear (25-25)
src/runtime/core/argumentparser.h (2)
  • string (56-136)
  • string (138-148)
🪛 Clang (14.0.6)
src/runtime/core/argumentparser.cpp

[error] 1-1: 'core/argumentparser.h' file not found

(clang-diagnostic-error)

🪛 GitHub Check: SonarCloud Code Analysis
src/runtime/core/argumentparser.cpp

[warning] 25-25: Define each identifier in a dedicated statement.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-o&open=AZqYeAOL_X5fEBrIKO-o&pullRequest=1


[warning] 276-276: Change this raw for-loop to a range for-loop or an "std::ranges::for_each".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-3&open=AZqYeAOL_X5fEBrIKO-3&pullRequest=1


[warning] 124-124: Use "std::format" or "std::format_to_n" instead of "sprintf". Use "std::format_to" only if buffer size is guaranteed to be sufficient.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-u&open=AZqYeAOL_X5fEBrIKO-u&pullRequest=1


[warning] 181-181: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-z&open=AZqYeAOL_X5fEBrIKO-z&pullRequest=1


[warning] 165-165: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-y&open=AZqYeAOL_X5fEBrIKO-y&pullRequest=1


[warning] 51-51: Use the init-statement to declare "error" inside the if statement.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-p&open=AZqYeAOL_X5fEBrIKO-p&pullRequest=1


[warning] 196-196: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-0&open=AZqYeAOL_X5fEBrIKO-0&pullRequest=1


[failure] 278-278: Refactor this code to not nest more than 3 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-2&open=AZqYeAOL_X5fEBrIKO-2&pullRequest=1


[failure] 46-46: Refactor this function to reduce its Cognitive Complexity from 43 to the 25 allowed.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-r&open=AZqYeAOL_X5fEBrIKO-r&pullRequest=1


[warning] 47-47: Do not use the constructor's initializer list for data member "mCurrentIndex". Use the in-class initializer instead.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-n&open=AZqYeAOL_X5fEBrIKO-n&pullRequest=1


[warning] 61-61: Use the init-statement to declare "pos" inside the if statement.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-q&open=AZqYeAOL_X5fEBrIKO-q&pullRequest=1


[warning] 122-122: Use "std::string" instead of a C-style char array.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-v&open=AZqYeAOL_X5fEBrIKO-v&pullRequest=1


[warning] 153-153: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-x&open=AZqYeAOL_X5fEBrIKO-x&pullRequest=1


[failure] 63-63: Refactor this code to not nest more than 3 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-s&open=AZqYeAOL_X5fEBrIKO-s&pullRequest=1


[warning] 47-47: Do not use the constructor's initializer list for data member "mIsValid". Use the in-class initializer instead.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-m&open=AZqYeAOL_X5fEBrIKO-m&pullRequest=1


[warning] 218-218: Change this raw for-loop to a range for-loop or an "std::ranges::for_each".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-1&open=AZqYeAOL_X5fEBrIKO-1&pullRequest=1

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build (ubuntu-latest, Release, gcc)
  • GitHub Check: build (windows-latest, Release, cl)
  • GitHub Check: build (ubuntu-latest, Release, clang)

Comment on lines +132 to +143
bool ArgumentParser::getNext(std::string &arg, std::string &value) {
if (mSupportedArguments.isEmpty() || mStoredArguments.isEmpty() ||
mCurrentIndex >= mSupportedArguments.size()) {
return false;
}

arg = mSupportedArguments[mCurrentIndex].mArgument;
value = mStoredArguments[mCurrentIndex];
++mCurrentIndex;

return true;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

getNext mixes supported and detected arguments and can read past mStoredArguments

getNext bounds mCurrentIndex by mSupportedArguments.size() but indexes into mStoredArguments. If the number of supported arguments exceeds the number of detected arguments (very likely), a second or later call to getNext can access mStoredArguments[mCurrentIndex] out-of-bounds, and argument names/values are not correctly paired.

You probably want to iterate over the detected arguments instead:

 bool ArgumentParser::getNext(std::string &arg, std::string &value) {
-    if (mSupportedArguments.isEmpty() || mStoredArguments.isEmpty() ||
-            mCurrentIndex >= mSupportedArguments.size()) {
+    if (mDetectedArgs.isEmpty() || mStoredArguments.isEmpty() ||
+            mCurrentIndex >= mDetectedArgs.size()) {
         return false;
     }
 
-    arg = mSupportedArguments[mCurrentIndex].mArgument;
+    arg = mDetectedArgs[mCurrentIndex];
     value = mStoredArguments[mCurrentIndex];
     ++mCurrentIndex;
 
     return true;
 }

This keeps arg and value aligned with what was actually parsed and prevents out-of-bounds access on mStoredArguments.

🤖 Prompt for AI Agents
In src/runtime/core/argumentparser.cpp around lines 132-143, getNext currently
bounds mCurrentIndex against mSupportedArguments.size() but indexes into
mStoredArguments, which can cause out-of-bounds access and mispaired
names/values; change the logic to iterate over the detected arguments instead by
using mStoredArguments.size() as the bound, and when setting arg try to retrieve
the matching supported argument (if mCurrentIndex < mSupportedArguments.size()
use mSupportedArguments[mCurrentIndex].mArgument, otherwise fall back to a safe
default such as an empty string or the stored key), increment mCurrentIndex and
return true; ensure the initial emptiness check uses mStoredArguments.isEmpty()
and that mCurrentIndex is compared to mStoredArguments.size() to prevent reading
past the parsed values.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/runtime/core/argumentparser.cpp (2)

139-150: Critical: getNext still mixes supported and detected arguments (unresolved).

This function bounds-checks against mSupportedArguments.size() but reads from mStoredArguments, and returns the wrong argument names. If you have 10 supported arguments but only 2 detected, this breaks after the second call. More importantly, it returns supported argument names instead of detected ones, mispairing names with values.

Apply this diff to iterate over detected arguments:

 bool ArgumentParser::getNext(std::string &arg, std::string &value) {
-    if (mSupportedArguments.isEmpty() || mStoredArguments.isEmpty() ||
-            mCurrentIndex >= mSupportedArguments.size()) {
+    if (mDetectedArgs.isEmpty() || mStoredArguments.isEmpty() ||
+            mCurrentIndex >= mDetectedArgs.size()) {
         return false;
     }
 
-    arg = mSupportedArguments[mCurrentIndex].mArgument;
+    arg = mDetectedArgs[mCurrentIndex];
     value = mStoredArguments[mCurrentIndex];
     ++mCurrentIndex;
 
     return true;
 }

188-201: Major: Static Dummy variable is not thread-safe (unresolved).

The static std::string Dummy is shared across all calls and threads, creating a race condition. Additionally, callers cannot distinguish between "argument not found" and "argument found with empty value."

Consider returning std::optional<std::string> if C++17 is available:

#include <optional>

std::optional<std::string> ArgumentParser::getArgument(const std::string &arg) const {
    if (arg.empty()) {
        return std::nullopt;
    }
    
    for (uint32_t index = 0; index < mDetectedArgs.size(); ++index) {
        if (mDetectedArgs[index] == arg) {
            return mStoredArguments[index];
        }
    }
    
    return std::nullopt;
}

Otherwise, make Dummy thread-local:

 const std::string &ArgumentParser::getArgument(const std::string &arg) const {
-    static std::string Dummy;
+    thread_local std::string Dummy;
🧹 Nitpick comments (1)
src/runtime/core/argumentparser.cpp (1)

127-133: Replace unsafe sprintf with std::to_string.

Using sprintf with a fixed buffer risks overflow and is not idiomatic modern C++. Since you're only formatting a single integer, std::to_string is simpler and safer.

Apply this diff:

         if (numargs > 0) {
             helpMsg += "\n\tNumber of arguments: ";
-            char buffer[512];
-            memset(buffer, '\0', 512);
-            sprintf(buffer, "%d\n", numargs);
-            helpMsg += std::string(buffer);
+            helpMsg += std::to_string(numargs) + "\n";
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d4caa9 and 9e2479d.

📒 Files selected for processing (1)
  • src/runtime/core/argumentparser.cpp (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/runtime/core/argumentparser.cpp (2)
src/runtime/core/tokenizer.cpp (6)
  • tokenize (29-50)
  • tokenize (29-29)
  • tokenize (52-60)
  • tokenize (52-52)
  • clear (25-27)
  • clear (25-25)
src/runtime/core/argumentparser.h (2)
  • string (56-136)
  • string (138-148)
🪛 Clang (14.0.6)
src/runtime/core/argumentparser.cpp

[error] 1-1: 'core/argumentparser.h' file not found

(clang-diagnostic-error)

🪛 GitHub Check: SonarCloud Code Analysis
src/runtime/core/argumentparser.cpp

[warning] 283-283: Change this raw for-loop to a range for-loop or an "std::ranges::for_each".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-3&open=AZqYeAOL_X5fEBrIKO-3&pullRequest=1


[warning] 25-25: Define each identifier in a dedicated statement.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-o&open=AZqYeAOL_X5fEBrIKO-o&pullRequest=1


[warning] 188-188: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-z&open=AZqYeAOL_X5fEBrIKO-z&pullRequest=1


[warning] 203-203: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-0&open=AZqYeAOL_X5fEBrIKO-0&pullRequest=1


[warning] 47-47: Do not use the constructor's initializer list for data member "mCurrentIndex". Use the in-class initializer instead.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-n&open=AZqYeAOL_X5fEBrIKO-n&pullRequest=1


[failure] 46-46: Refactor this function to reduce its Cognitive Complexity from 52 to the 25 allowed.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-r&open=AZqYeAOL_X5fEBrIKO-r&pullRequest=1


[failure] 285-285: Refactor this code to not nest more than 3 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-2&open=AZqYeAOL_X5fEBrIKO-2&pullRequest=1


[warning] 51-51: Use the init-statement to declare "error" inside the if statement.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-p&open=AZqYeAOL_X5fEBrIKO-p&pullRequest=1


[warning] 47-47: Do not use the constructor's initializer list for data member "mIsValid". Use the in-class initializer instead.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-m&open=AZqYeAOL_X5fEBrIKO-m&pullRequest=1


[warning] 62-62: Use the init-statement to declare "pos" inside the if statement.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-q&open=AZqYeAOL_X5fEBrIKO-q&pullRequest=1


[failure] 64-64: Refactor this code to not nest more than 3 if|for|do|while|switch statements.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-s&open=AZqYeAOL_X5fEBrIKO-s&pullRequest=1


[warning] 172-172: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-y&open=AZqYeAOL_X5fEBrIKO-y&pullRequest=1


[warning] 131-131: Use "std::format" or "std::format_to_n" instead of "sprintf". Use "std::format_to" only if buffer size is guaranteed to be sufficient.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-u&open=AZqYeAOL_X5fEBrIKO-u&pullRequest=1


[warning] 57-57: Replace the redundant type with "auto".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZrBz7VNf2WJSxOk05Bl&open=AZrBz7VNf2WJSxOk05Bl&pullRequest=1


[warning] 225-225: Change this raw for-loop to a range for-loop or an "std::ranges::for_each".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-1&open=AZqYeAOL_X5fEBrIKO-1&pullRequest=1


[warning] 129-129: Use "std::string" instead of a C-style char array.

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-v&open=AZqYeAOL_X5fEBrIKO-v&pullRequest=1


[warning] 160-160: Replace this const reference to "std::string" by a "std::string_view".

See more on https://sonarcloud.io/project/issues?id=kimkulling_Segfault&issues=AZqYeAOL_X5fEBrIKO-x&open=AZqYeAOL_X5fEBrIKO-x&pullRequest=1

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build (ubuntu-latest, Release, gcc)
  • GitHub Check: build (ubuntu-latest, Release, clang)
🔇 Additional comments (1)
src/runtime/core/argumentparser.cpp (1)

22-44: LGTM! Past issue resolved.

The premature size check has been correctly removed. The function now tokenizes first, then validates the sizes, which is the proper order.

Comment on lines +84 to +94
if (mIsValid) {
if ((mCurrentIndex + 1) < static_cast<uint32_t>(argc)) {
++mCurrentIndex;
mDetectedArgs.add(argument);
std::string content(ppArgv[mCurrentIndex]);
mStoredArguments.add(content);
} else {
mDetectedArgs.add(argument);
mStoredArguments.add("");
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Only one value is stored for multi-value arguments.

When numValues > 1, the validation loop (lines 65-81) correctly checks that multiple values are present, but the storage logic (lines 84-94) only stores the first value. Arguments expecting multiple values will lose all but the first.

Apply this diff to store all expected values:

                     // Store the data if its valid
                     if (mIsValid) {
-                        if ((mCurrentIndex + 1) < static_cast<uint32_t>(argc)) {
-                            ++mCurrentIndex;
-                            mDetectedArgs.add(argument);
-                            std::string content(ppArgv[mCurrentIndex]);
-                            mStoredArguments.add(content);
-                        } else {
-                            mDetectedArgs.add(argument);
-                            mStoredArguments.add("");
+                        mDetectedArgs.add(argument);
+                        
+                        const uint32_t numValues = getNumValues(argument);
+                        if (numValues > 0) {
+                            std::string combinedValues;
+                            for (uint32_t i = 0; i < numValues && (mCurrentIndex + 1 + i) < argcU; ++i) {
+                                if (i > 0) combinedValues += " ";
+                                combinedValues += ppArgv[mCurrentIndex + 1 + i];
+                            }
+                            mStoredArguments.add(combinedValues);
+                            mCurrentIndex += numValues;
+                        } else {
+                            if ((mCurrentIndex + 1) < argcU) {
+                                ++mCurrentIndex;
+                                mStoredArguments.add(ppArgv[mCurrentIndex]);
+                            } else {
+                                mStoredArguments.add("");
+                            }
                         }
                     }
🤖 Prompt for AI Agents
In src/runtime/core/argumentparser.cpp around lines 84-94: the code currently
only stores a single value even when an argument expects multiple values;
replace the single-value store with logic that, after
mDetectedArgs.add(argument), loops for the argument's expected numValues and for
each iteration increments mCurrentIndex, reads ppArgv[mCurrentIndex] (or uses an
empty string if out of range), adds that string to mStoredArguments, and ensures
mCurrentIndex is advanced for every consumed value so all expected values are
stored (single-value arguments still consume exactly one entry).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/runtime/application/app.cpp (1)

30-35: Duplicate function definition will cause compilation error.

A second getEndLog() function is defined with the same signature as the one at lines 23-28. This creates a redefinition error and will prevent the code from compiling.

Remove one of the duplicate definitions. Based on the formatting consistency with getStartLog(), the first definition (lines 23-28) appears to be the intended one.

🔎 Proposed fix
-    std::string getEndLog() {
-        std::string entry = "===========================================================================\n";
-        entry.append(" SegFault run ended\n");
-        entry.append("===========================================================================");
-        return entry;
-    }
♻️ Duplicate comments (1)
src/runtime/application/app.cpp (1)

18-18: Version string contains a typo (letter 'l' instead of digit).

The version string "0.0.l" uses a lowercase letter 'l' instead of a digit. This should likely be "0.0.1" or the correct numeric version.

🧹 Nitpick comments (1)
src/runtime/renderer/rendercore.h (1)

24-26: Empty placeholder struct should be completed or documented.

The RenderPipelineState struct is currently empty and not actively used (its usage in DrawCommand at line 28 is commented out). This suggests incomplete work-in-progress.

Consider either:

  • Implementing the struct with the necessary state fields (shader program, blend state, depth/stencil settings, etc.)
  • Adding a TODO comment to track future implementation
  • Removing it if not yet needed

Would you like me to help outline typical render pipeline state fields based on common rendering APIs?

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e2479d and b50cec5.

📒 Files selected for processing (3)
  • src/runtime/application/app.cpp
  • src/runtime/renderer/RHIVulkan.cpp
  • src/runtime/renderer/rendercore.h
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/runtime/renderer/RHIVulkan.cpp
🧰 Additional context used
🧬 Code graph analysis (1)
src/runtime/application/app.cpp (1)
src/runtime/core/segfault.h (1)
  • logMessage (62-77)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build (windows-latest, Release, cl)
🔇 Additional comments (1)
src/runtime/application/app.cpp (1)

133-136: Good defensive null check in shutdown.

The early return when mSdlWindow is null prevents potential crashes from calling SDL_DestroyWindow on a null pointer. This is good defensive programming.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/runtime/application/app.cpp (1)

125-136: Memory leak: mRHI allocated but never cleaned up in shutdown.

mRHI is allocated with new at line 93 but neither its shutdown() method is called nor the object is deleted in shutdown(). Since RHI provides a public shutdown() method, it must be called before deletion to allow proper cleanup of its internal state (including mImpl).

Add the following cleanup before SDL teardown:

 void App::shutdown() {
     if (mSdlWindow == nullptr) {
         logMessage(LogType::Error, "Invalid application state, cannot shutdown.");
         return;
     }
+    
+    if (mRHI != nullptr) {
+        mRHI->shutdown();
+        delete mRHI;
+        mRHI = nullptr;
+    }
 
     SDL_DestroyWindow(mSdlWindow);
     mSdlWindow = nullptr;
     mState = ModuleState::Shutdown;
     SDL_Quit();
     logMessage(LogType::Print, getEndLog().c_str());
 }
♻️ Duplicate comments (1)
src/runtime/application/app.cpp (1)

18-18: Version string typo: 0.0.l should likely be 0.0.1

The version string uses the letter l instead of the digit 1. This appears to be a typo.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b50cec5 and 48ed952.

📒 Files selected for processing (2)
  • src/contrib/cppcore
  • src/runtime/application/app.cpp
✅ Files skipped from review due to trivial changes (1)
  • src/contrib/cppcore
🧰 Additional context used
🧬 Code graph analysis (1)
src/runtime/application/app.cpp (1)
src/runtime/core/segfault.h (1)
  • logMessage (62-77)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build (ubuntu-latest, Release, gcc)
  • GitHub Check: build (ubuntu-latest, Release, clang)
🔇 Additional comments (1)
src/runtime/application/app.cpp (1)

23-28: LGTM!

The end log banner formatting is consistent with the start log banner style.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
src/runtime/core/argumentparser.cpp (4)

48-51: Remove dead code: size check before tokenization.

Lines 48-51 check extractedDescs.size() != extractedArgs.size() immediately after declaring both arrays empty. This condition is always false (0 == 0) and has no effect. The actual validation correctly happens at lines 56-59 after tokenization.


111-121: Critical: Only one value stored for multi-value arguments.

When an argument expects numValues > 1, the validation loop (lines 92-108) correctly checks that multiple values are present, but the storage logic (lines 111-121) only stores the first value and doesn't advance mCurrentIndex by the number of consumed values. Arguments expecting multiple values will lose all but the first.

Store all expected values and advance the index accordingly:

🔎 Proposed fix
                     // Store the data if its valid
                     if (mIsValid) {
+                        mDetectedArgs.add(argument);
+                        
+                        const uint32_t numValues = getNumValues(argument);
+                        if (numValues > 0) {
+                            std::string combinedValues;
+                            for (uint32_t i = 0; i < numValues && (mCurrentIndex + 1 + i) < argcU; ++i) {
+                                if (i > 0) combinedValues += " ";
+                                combinedValues += ppArgv[mCurrentIndex + 1 + i];
+                            }
+                            mStoredArguments.add(combinedValues);
+                            mCurrentIndex += numValues;
+                        } else {
-                        if ((mCurrentIndex + 1) < static_cast<uint32_t>(argc)) {
-                            ++mCurrentIndex;
-                            mDetectedArgs.add(argument);
-                            std::string content(ppArgv[mCurrentIndex]);
-                            mStoredArguments.add(content);
-                        } else {
-                            mDetectedArgs.add(argument);
-                            mStoredArguments.add("");
+                            if ((mCurrentIndex + 1) < argcU) {
+                                ++mCurrentIndex;
+                                mStoredArguments.add(ppArgv[mCurrentIndex]);
+                            } else {
+                                mStoredArguments.add("");
+                            }
                         }
                     }

166-177: Critical: getNext mixes supported and detected arguments, causing out-of-bounds access.

getNext bounds mCurrentIndex by mSupportedArguments.size() but indexes into mStoredArguments. If the number of supported arguments exceeds the number of detected arguments (very likely), subsequent calls to getNext will access mStoredArguments[mCurrentIndex] out-of-bounds. Additionally, argument names and values will not be correctly paired.

Iterate over detected arguments instead:

🔎 Proposed fix
 bool ArgumentParser::getNext(std::string &arg, std::string &value) {
-    if (mSupportedArguments.isEmpty() || mStoredArguments.isEmpty() ||
-            mCurrentIndex >= mSupportedArguments.size()) {
+    if (mDetectedArgs.isEmpty() || mStoredArguments.isEmpty() ||
+            mCurrentIndex >= mDetectedArgs.size()) {
         return false;
     }
 
-    arg = mSupportedArguments[mCurrentIndex].mArgument;
+    arg = mDetectedArgs[mCurrentIndex];
     value = mStoredArguments[mCurrentIndex];
     ++mCurrentIndex;
 
     return true;
 }

215-228: Static Dummy variable is not thread-safe.

The static std::string Dummy at line 216 is shared across all calls to getArgument. If multiple threads call this method, there's a potential race condition. Additionally, callers cannot distinguish between an argument with an empty value and an argument that was not found.

Consider one of these alternatives:

Option 1: Return std::optional<std::string> (requires C++17):

🔎 Proposed fix using std::optional
+#include <optional>
+
-const std::string &ArgumentParser::getArgument(const std::string &arg) const {
-    static std::string Dummy;
+std::optional<std::string> ArgumentParser::getArgument(const std::string &arg) const {
     if (arg.empty()) {
-        return Dummy;
+        return std::nullopt;
     }
 
     for (uint32_t index = 0; index < mDetectedArgs.size(); ++index) {
         if (mDetectedArgs[index] == arg) {
             return mStoredArguments[index];
         }
     }
 
-    return Dummy;
+    return std::nullopt;
 }

Update the header declaration accordingly.

Option 2: Use thread_local if C++11:

🔎 Alternative fix using thread_local
 const std::string &ArgumentParser::getArgument(const std::string &arg) const {
-    static std::string Dummy;
+    thread_local std::string Dummy;
     if (arg.empty()) {
         return Dummy;
     }
🧹 Nitpick comments (2)
src/runtime/core/argumentparser.cpp (2)

156-159: Consider using std::to_string instead of sprintf.

The sprintf usage here is safe but could be replaced with the modern C++ std::to_string(numargs) for clearer intent and to avoid manual buffer management.

🔎 Proposed refactor
         if (numargs > 0) {
             helpMsg += "\n\tNumber of arguments: ";
-            char buffer[512];
-            memset(buffer, '\0', 512);
-            sprintf(buffer, "%d\n", numargs);
-            helpMsg += std::string(buffer);
+            helpMsg += std::to_string(numargs);
+            helpMsg += "\n";
         }

272-272: Consider using std::stoi instead of atoi for better error handling.

atoi returns 0 on error or if the input is "0", making it impossible to distinguish between valid zero and parse failure. std::stoi with exception handling would be more robust.

🔎 Proposed refactor
     if (!paraDefOpen) {
-        numPara = atoi(paraStr.c_str());
+        try {
+            numPara = static_cast<uint32_t>(std::stoi(paraStr));
+        } catch (const std::exception&) {
+            numPara = 0;
+            return false;
+        }
     } else {
         return false;
     }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48ed952 and f0e8c40.

📒 Files selected for processing (5)
  • src/contrib/cppcore
  • src/runtime/application/app.cpp
  • src/runtime/core/argumentparser.cpp
  • src/runtime/core/argumentparser.h
  • src/runtime/renderer/RHIVulkan.cpp
💤 Files with no reviewable changes (1)
  • src/runtime/renderer/RHIVulkan.cpp
✅ Files skipped from review due to trivial changes (1)
  • src/contrib/cppcore
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/runtime/application/app.cpp
🧰 Additional context used
🧬 Code graph analysis (1)
src/runtime/core/argumentparser.h (1)
src/runtime/core/argumentparser.cpp (26)
  • ArgumentParser (73-135)
  • Argument (34-37)
  • Argument (39-42)
  • showHelp (137-164)
  • showHelp (137-137)
  • getNext (166-177)
  • getNext (166-166)
  • isSupported (187-197)
  • isSupported (187-187)
  • getNumValues (199-213)
  • getNumValues (199-199)
  • argument (87-87)
  • getArgument (215-228)
  • getArgument (215-215)
  • hasArgument (230-239)
  • hasArgument (230-230)
  • hasValidArgs (241-243)
  • hasValidArgs (241-241)
  • parseArgParameter (245-278)
  • parseArgParameter (245-245)
  • getBlankArgument (280-301)
  • getBlankArgument (280-280)
  • validateArguments (303-326)
  • validateArguments (303-303)
  • setInvalid (328-330)
  • setInvalid (328-328)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build (ubuntu-latest, Release, gcc)
  • GitHub Check: build (ubuntu-latest, Release, clang)
🔇 Additional comments (1)
src/runtime/core/argumentparser.h (1)

1-172: Header declarations look sound.

The header file correctly declares the ArgumentParser API. However, there are critical implementation issues in the corresponding .cpp file that need to be addressed (see comments on argumentparser.cpp).

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 2, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
2 Security Hotspots

See analysis details on SonarQube Cloud

@kimkulling kimkulling merged commit 1623293 into main Jan 3, 2026
2 of 6 checks passed
@kimkulling kimkulling deleted the feature/argument_parser branch January 3, 2026 18:39
@kimkulling kimkulling self-assigned this Jan 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants