Skip to content

refactor(core): Resolve clang-tidy violations in string_utils.#2003

Open
Nathan903 wants to merge 18 commits intoy-scope:mainfrom
Nathan903:refactor/string-utils-lint
Open

refactor(core): Resolve clang-tidy violations in string_utils.#2003
Nathan903 wants to merge 18 commits intoy-scope:mainfrom
Nathan903:refactor/string-utils-lint

Conversation

@Nathan903
Copy link
Contributor

@Nathan903 Nathan903 commented Feb 17, 2026

Description

Resolve clang-tidy violations in the string_utils library (string_utils.hpp, string_utils.cpp) and its unit test (test-string_utils.cpp), and update taskfiles/lint.yaml to remove these files from the exclusion list.

Checklist

  • The PR satisfies the contribution guidelines.
  • This is a breaking change and that has been indicated in the PR title, OR this isn't a breaking change.
  • Necessary docs have been updated, OR no docs need to be updated.

Validation performed

  1. Built components/core successfully to ensure no compilation errors were introduced.
  2. Ran the unit test components/core/build/tests/test-string_utils to verify functionality matches previous behavior.
  3. Ran clang-tidy on the modified files to verify no warnings remain.

Summary by CodeRabbit

Release Notes

  • Refactor

    • Modernized string utility function signatures to use contemporary C++ conventions and improved type safety measures
    • Updated string utility test suite with consistent naming conventions and modern code style
    • Enhanced compiler checks for string operation return values
  • Chores

    • Enabled static code analysis coverage for string utility modules to maintain code quality standards

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 17, 2026

Walkthrough

Modernizes C++ function signatures in string utilities by converting them to use auto with trailing return type syntax and adding [[nodiscard]] attributes to enforce value-usage checks. Test code is refactored to adopt snake_case naming conventions. Static analysis exclusions for these modules are removed to enable linting checks.

Changes

Cohort / File(s) Summary
String utilities modernization
components/core/src/clp/string_utils/string_utils.hpp, components/core/src/clp/string_utils/string_utils.cpp
Refactored function signatures to use trailing return type syntax (e.g., auto func(...) -> return_type) and added [[nodiscard]] attributes to multiple public functions including is_wildcard(), wildcard_match_unsafe(), find_first_of(), replace_characters(), clean_up_wildcard_search_string(), unescape_string(), and convert_string_to_int(). Added [[nodiscard]] to advance_tame_to_next_match(). Logic and control flow remain unchanged.
Test code refactoring
components/core/tests/test-string_utils.cpp
Renamed test variables from CamelCase to snake_case (e.g., tameStringtame_string, wildStringwild_string). Updated variable initialization to use brace initialization style. Expanded includes and refactored performance test timing logic to match new naming conventions. No functional test logic changes.
Linting configuration
taskfiles/lint.yaml
Removed three files from C++ static analysis exclusion patterns: clp/string_utils/string_utils.cpp, clp/string_utils/string_utils.hpp, and test-string_utils.cpp, enabling clang-tidy analysis for these modules.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: refactoring string_utils to resolve clang-tidy violations through modernizing function signatures with auto return types and [[nodiscard]] attributes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@Nathan903 Nathan903 changed the title refactor(core): Resolve clang-tidy violations in string_utils refactor(core): Resolve clang-tidy violations in string_utils. Feb 17, 2026
@Nathan903 Nathan903 marked this pull request as ready for review February 18, 2026 16:26
@Nathan903 Nathan903 requested a review from a team as a code owner February 18, 2026 16:26
Copy link
Contributor

@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: 7

Caution

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

⚠️ Outside diff range comments (1)
components/core/src/clp/string_utils/string_utils.cpp (1)

144-148: ⚠️ Potential issue | 🟡 Minor

to_lower definition is inconsistent with the updated header declaration.

The header (line 84) was updated in this PR to auto to_lower(std::string& str) -> void;, but the definition here still uses the traditional void to_lower(string& str) form. Every other function definition in this file uses the trailing-return-type style; this is the only outlier.

♻️ Proposed fix
-void to_lower(string& str) {
+auto to_lower(string& str) -> void {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/core/src/clp/string_utils/string_utils.cpp` around lines 144 -
148, The definition of to_lower uses the old-style signature but the header
declares it with a trailing-return-type; update the function definition for
to_lower(std::string& str) to use the same trailing-return-type form (auto
to_lower(std::string& str) -> void) so it matches the header and other functions
in string_utils.cpp; keep the body unchanged (std::transform with std::tolower)
and ensure the parameter type matches the header's std::string reference and the
function name to_lower is used exactly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/core/src/clp/string_utils/string_utils.cpp`:
- Around line 150-158: Remove the redundant docstring above the is_wildcard
function in string_utils.cpp because the canonical documentation belongs in
string_utils.hpp; delete the comment block shown and keep the implementation
as-is (auto is_wildcard(char c) -> bool { ... }), and if you prefer the more
precise phrasing ("true if '?' or '*'") migrate that wording into the header
comment for is_wildcard in string_utils.hpp instead of duplicating it in the
.cpp.
- Around line 264-266: The bug is that the flag is_escaped (declared alongside
char w and t) is never reset between iterations so a prior escape makes
subsequent '?' lose wildcard behavior; fix by making is_escaped a
block-local/iteration-local value (or explicitly reset it to false at the top of
the loop) so it accurately reflects whether the current w came from a '\' escape
before the mismatch check that uses is_escaped (see variables w, t, is_escaped
and the mismatch logic around the existing '?' handling), and add a unit test
case with wild="\\a?" and tame="ab" expecting true.
- Around line 32-37: The definition of advance_tame_to_next_match is missing the
[[nodiscard]] attribute present on its forward declaration; update the function
definition signature for advance_tame_to_next_match(char const*& tame_current,
char const*& tame_bookmark, char const* tame_end, char const*& wild_current) to
include [[nodiscard]] so it matches the declaration and the rest of the updated
functions.

In `@components/core/tests/test-string_utils.cpp`:
- Around line 409-410: Remove the duplicated assertions that call
wildcard_match_unsafe_case_sensitive with identical arguments: replace the
second occurrence of wildcard_match_unsafe_case_sensitive("ababac", "*abac*")
and the second occurrence of wildcard_match_unsafe_case_sensitive("abAbac",
"*Abac*") with either a removal or a distinct test case input to exercise
different behavior (e.g., different string or pattern) so each assertion adds
coverage rather than duplicating the same check.
- Around line 242-244: The GIVEN descriptions say the pattern starts/ends with
"?*" but the test uses "*?" due to copy-paste; update the test cases that call
wildcard_match_unsafe_case_sensitive so the wild_string matches the description:
change the first case's wild_string from "*?cd" to "?*cd" (GIVEN "Wild begins
with \"?*\"...") and change the later case's wild_string from "abc*?" to "abc?*"
(GIVEN "Wild ends with \"?*\"..."), leaving tame_string and the REQUIRE
assertions unchanged.
- Line 403: Replace negated expressions using the unary ! with the preferred
form "false == <expression>" throughout the test file: locate uses like
all_passed &= !wildcard_match_unsafe_case_sensitive(...); and change them to
all_passed &= (false == wildcard_match_unsafe_case_sensitive(...)); apply this
consistently for all occurrences (e.g., lines referencing
wildcard_match_unsafe_case_sensitive and any other function calls negated with
!) and keep the parentheses to preserve precedence around the comparison; ensure
you update every listed occurrence rather than adding new logical negation.
- Around line 385-391: The test contains a duplicate assertion using the same
tame_string/wild_string pair; update the second sub-case in the GIVEN block
("All lower case tame and mixed lower and upper case wild") so it uses the
intended mixed-case pattern (e.g., set wild_string to "A?C*") and keep the
REQUIRE calling wildcard_match_unsafe(tame_string, wild_string,
is_case_sensitive) to verify case-insensitive matching; modify the second
assignment of wild_string (and optionally tame_string) instead of duplicating
the first pair so the test covers the mixed upper-case 'C'.

---

Outside diff comments:
In `@components/core/src/clp/string_utils/string_utils.cpp`:
- Around line 144-148: The definition of to_lower uses the old-style signature
but the header declares it with a trailing-return-type; update the function
definition for to_lower(std::string& str) to use the same trailing-return-type
form (auto to_lower(std::string& str) -> void) so it matches the header and
other functions in string_utils.cpp; keep the body unchanged (std::transform
with std::tolower) and ensure the parameter type matches the header's
std::string reference and the function name to_lower is used exactly.

Comment on lines +32 to +37
inline auto advance_tame_to_next_match(
char const*& tame_current,
char const*& tame_bookmark,
char const* tame_end,
char const*& wild_current
) {
) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

[[nodiscard]] missing from the definition of advance_tame_to_next_match.

The forward declaration (line 25) correctly carries [[nodiscard]], but the definition at line 32 omits it. The PR explicitly adds [[nodiscard]] to both declarations and definitions for all other updated functions; this definition is the lone exception.

♻️ Proposed fix
-inline auto advance_tame_to_next_match(
+[[nodiscard]] inline auto advance_tame_to_next_match(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
inline auto advance_tame_to_next_match(
char const*& tame_current,
char const*& tame_bookmark,
char const* tame_end,
char const*& wild_current
) {
) -> bool {
[[nodiscard]] inline auto advance_tame_to_next_match(
char const*& tame_current,
char const*& tame_bookmark,
char const* tame_end,
char const*& wild_current
) -> bool {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/core/src/clp/string_utils/string_utils.cpp` around lines 32 - 37,
The definition of advance_tame_to_next_match is missing the [[nodiscard]]
attribute present on its forward declaration; update the function definition
signature for advance_tame_to_next_match(char const*& tame_current, char const*&
tame_bookmark, char const* tame_end, char const*& wild_current) to include
[[nodiscard]] so it matches the declaration and the rest of the updated
functions.

Copy link
Contributor

Choose a reason for hiding this comment

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

No we only add [[nodiscard]] to declarations, not definitions.


template <std::integral integer_t>
bool convert_string_to_int(std::string_view raw, integer_t& converted) {
[[nodiscard]] auto convert_string_to_int(std::string_view raw, integer_t& converted) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
[[nodiscard]] auto convert_string_to_int(std::string_view raw, integer_t& converted) -> bool {
auto convert_string_to_int(std::string_view raw, integer_t& converted) -> bool {

char const* tame_bookmark{nullptr};
char const* wild_bookmark{nullptr};
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
char const* tame_end = tame_current + tame_length;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
char const* tame_end = tame_current + tame_length;
char const* tame_end{tame_current + tame_length};

// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
char const* tame_end = tame_current + tame_length;
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
char const* wild_end = wild_current + wild_length;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
char const* wild_end = wild_current + wild_length;
char const* wild_end{wild_current + wild_length};

// Handle escaped characters
if ('\\' == w) {
is_escaped = true;
bool is_escaped = '\\' == w;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
bool is_escaped = '\\' == w;
auto const is_escaped = ('\\' == w);

or

Suggested change
bool is_escaped = '\\' == w;
bool const is_escaped{'\\' == w};

Comment on lines +216 to +217
// We convert to lowercase (rather than uppercase) anticipating that
// callers use lowercase more frequently, so little will need to change.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// We convert to lowercase (rather than uppercase) anticipating that
// callers use lowercase more frequently, so little will need to change.
// We convert to lowercase (rather than uppercase) anticipating that callers
// use lowercase more frequently, so little will need to change.

Reflow comments for the 80 character line limit used in this file.

std::string in,
std::string const& expected) {
replace_unescaped_char(escape_char, from_char, to_char, in);
REQUIRE((in == expected));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
REQUIRE(in == expected);

Find all occurrences where REQUIRE wraps comparisons in double parentheses and remove the extra parentheses. After upgrading from Catch2 v2 to v3, the additional parentheses around comparison expressions are no longer required.

}
++c;
}
REQUIRE(unescaped_string == unescape_string(escaped_string));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
REQUIRE(unescape_string(escaped_string) == unescaped_string);

tameString = "abcd", wildString = "a*";
REQUIRE(wildcard_match_unsafe_case_sensitive(tameString, wildString) == true);
tame_string = "abcd", wild_string = "a*";
REQUIRE(wildcard_match_unsafe_case_sensitive(tame_string, wild_string) == true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
REQUIRE(wildcard_match_unsafe_case_sensitive(tame_string, wild_string) == true);
REQUIRE(wildcard_match_unsafe_case_sensitive(tame_string, wild_string));

Let's remove all redundant ==true

allPassed &= !wildcard_match_unsafe_case_sensitive("a12b12", "a12b");
allPassed &= wildcard_match_unsafe_case_sensitive("a12b12", "*12*12*");
REQUIRE(allPassed == true);
all_passed &= wildcard_match_unsafe_case_sensitive("abcccd", "*ccd");
Copy link
Contributor

Choose a reason for hiding this comment

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

After giving this some thought, I think there's no upside to report a single status for a bunch of tests. You cannot pinpoint the exact places where you fail.
Let's replace all passing function calls with REQUIRE(...) and all failing ones with REQUIRE_FALSE(...). Hopefully you can make use of AI tools to help you do this chore.

Comment on lines +604 to +609
test_reps = n_reps;
t1 = high_resolution_clock::now();
while (testReps--) {
BOOST_FOREACH (boost::tie(tameStr, wildStr), boost::combine(tameVec, wildVec)) {
allPassed_currentImplementation
&= wildcard_match_unsafe_case_sensitive(tameStr, wildStr);
while (test_reps-- != 0) {
BOOST_FOREACH (boost::tie(tame_str, wild_str), boost::combine(tame_vec, wild_vec)) {
all_passed_current_implementation
&= wildcard_match_unsafe_case_sensitive(tame_str, wild_str);
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess for this block it makes sense to use all passed variable due to the sheer number of iterations.

) {
) -> bool {
auto w = *wild_current;
if ('?' != w) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if ('?' != w) {
if (cSingleCharWildcard != w) {

let's make use of the constants inside constants.hpp

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

Comments