Skip to content

fix(StringTools): edge cases in split_up, remove_quotes, append_codepoint, escape_detect#1364

Merged
henryiii merged 2 commits into
mainfrom
fix/stringtools-edge-cases
Jul 1, 2026
Merged

fix(StringTools): edge cases in split_up, remove_quotes, append_codepoint, escape_detect#1364
henryiii merged 2 commits into
mainfrom
fix/stringtools-edge-cases

Conversation

@henryiii

Copy link
Copy Markdown
Collaborator

🤖 AI text below 🤖

Part of #1357

Fixes four verified string-handling edge cases in include/CLI/impl/StringTools_inl.hpp, all confirmed against the current code.

1. split_up dropped the character after a closing quote/bracket

When a token started with a bracket/quote, the code did output.push_back(str.substr(0, end + 1)) then unconditionally str = str.substr(end + 2), assuming str[end + 1] was a delimiter. For e.g. split_up("\"abc\"def") the d vanished. Now we only skip position end + 1 when it is an actual delimiter (the local find_ws predicate); otherwise we resume from end + 1, so the remainder starts the next token and no characters are lost. New tests SplitUp: TrailingAfterQuote, TrailingAfterBracket, and DelimiterAfterQuote in tests/HelpersTest.cpp.

2. remove_quotes(std::vector<std::string>&) called front()/back() on empty strings (UB)

The App parse path erases empty strings first, but generate_parents (Config_inl.hpp) does not: split_up(section, '.') on a section like [a..b] yields empty segments that reached arg.front() on "". Fixed by skipping empty strings in the loop. New test StringBased: EmptySectionSegment in tests/ConfigFileTest.cpp parses [a..b] and asserts it does not crash and still finds the value.

3. append_codepoint silently emitted nothing for code points above 0x10FFFF

A \U escape computing a code >= 0x110000 fell off the end of the if/else ladder and disappeared, unlike the surrogate case which throws. Added a final else that throws std::invalid_argument. Also corrected the surrogate error message (was "are not valid UTF-8" -> "are not valid code points", since these are invalid code points, not bad UTF-8). New assertions in StringTools: unicode_literals cover \U00110000, \UFFFFFFFF, and the largest valid point \U0010FFFF.

4. escape_detect unsigned wraparound at offset 0

When the trigger char (=/:) is the first character, str.find_last_of("-/ \"'\", offset - 1)passednposand searched the whole string (including characters after the trigger), potentially matching a later-//and wrongly rewriting the leading char. Now returnsoffset + 1early whenoffset == 0. New test StringTools: escape_detect`.

Test change (old lossy behavior)

AppTest.cpp OneStringEqualVersionSingleStringQuotedEscapedCharacters had a stray trailing " after a closing backtick-literal: -m=`"quoted\n string"`". That unbalanced quote was previously silently swallowed by the split_up bug (item 1). With the fix it is preserved as a token and rejected as an unexpected argument. The trailing " is malformed input, so I removed it from the test input; the meaningful assertions are unchanged.

All tests pass: full ctest is 24/24 green (HelpersTest, ConfigFileTest, StringParseTest, AppTest included).

🤖 Generated with Claude Code

…quotes, append_codepoint, escape_detect

- split_up no longer drops a non-delimiter character following a closing
  quote/bracket; it only skips the next position when it is an actual delimiter.
- remove_quotes(vector) skips empty strings to avoid front()/back() UB reachable
  from config sections with consecutive separators (e.g. [a..b]).
- append_codepoint throws on code points above 0x10FFFF instead of silently
  emitting nothing; surrogate error message now refers to invalid code points
  rather than UTF-8.
- escape_detect returns early when the trigger char is at offset 0, avoiding an
  unsigned wraparound that searched the whole string.

Assisted-by: ClaudeCode:claude-opus-4-8
The previous fix for character loss after a closing quote/bracket resumed
parsing from the very next character. When that character is itself a
quote/bracket opening character (e.g. a third backtick in ```++), this
re-opened a fresh, potentially unterminated, quoted sequence that swallowed
following delimiters and merged separate tokens into one. That changed
command-line parsing of some fuzz-corpus inputs and broke the FuzzFailTest
round-trip invariant (app_roundtrip_custom index 14).

Narrow the fix so an ordinary trailing character is still retained, but a
quote/bracket opening character immediately after a close is skipped like the
delimiter case, matching the original well-behaved splitting.

Assisted-by: ClaudeCode:claude-opus-4.8
@henryiii henryiii marked this pull request as ready for review June 16, 2026 04:17
@henryiii henryiii merged commit 68692cf into main Jul 1, 2026
73 checks passed
@henryiii henryiii deleted the fix/stringtools-edge-cases branch July 1, 2026 20:15
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.

1 participant