Skip to content

fix: type-conversion edge cases in TypeTools and Option reduce#1362

Merged
henryiii merged 1 commit into
mainfrom
fix/typetools-conversions
Jul 1, 2026
Merged

fix: type-conversion edge cases in TypeTools and Option reduce#1362
henryiii merged 1 commit into
mainfrom
fix/typetools-conversions

Conversation

@henryiii

Copy link
Copy Markdown
Collaborator

🤖 AI text below 🤖

Fixes five verified type-conversion bugs in TypeTools.hpp and Option_inl.hpp, each with a regression test.

  1. Unsigned integral_conversion accepted " -1" as UINT64_MAX. The old guard only checked input.front() == '-', but strtoull skips leading whitespace and silently wraps negatives. Now the input is rejected when its first non-whitespace character is '-'. (include/CLI/TypeTools.hpp)

  2. Float lexical_cast converted whitespace-only strings to 0.0 successfully. When strtold performs no conversion the end pointer equals the start; the trailing-whitespace skip loop then walked to the end and returned true. Now we return false when nothing was consumed (val == input.c_str()). (include/CLI/TypeTools.hpp)

  3. Uninitialized v2 could be inserted into a container-of-pairs (UB). With an odd element count the second conversion was skipped while retval stayed true, inserting {v1, v2} with v2 never assigned. Both values are now value-initialized and a missing second element sets retval = false, so it raises a ConversionError instead of silently inserting {key, 0}. Reachable via add_result("onlykey"); results(map);. (include/CLI/TypeTools.hpp)

  4. sum_string_vector broke for integer sums >= 1e16 and lost precision above 2^53. Sums accumulated as double and printed at precision 16, rendering large totals in scientific notation that integral_conversion could not parse. An int64_t accumulation (with overflow check) is now tried first, falling back to the existing double path for floats/flags/non-numeric input. (include/CLI/TypeTools.hpp)

  5. Join policy in _reduce_results tested results_.size() instead of original.size(). Option::results(T&) reduces results synthesized from default_str_ while results_ is empty, so the join was skipped (yielding "a" instead of "a\nb"). Now tests original.size() > 1 like every other policy branch. (include/CLI/impl/Option_inl.hpp)

No false positives — all five reproduced against the code before the fix.

Tests added in tests/HelpersTest.cpp (unsigned leading-whitespace negative, whitespace-only float, odd-count pair container, sum_string_vector) and tests/OptionTypeTest.cpp (Join + default_str via results(), large-integer Sum policy).

Part of #1357

Fix five verified type-conversion bugs:

- Reject unsigned integral_conversion when the first non-whitespace
  character is '-' so " -1" no longer wraps to UINT64_MAX.
- Fail float lexical_cast when strtold consumes nothing, so
  whitespace-only strings no longer convert to 0.0.
- Value-initialize the pair elements in container-of-pairs
  lexical_conversion and fail on a missing second element, avoiding
  inserting an uninitialized value (UB).
- Accumulate sum_string_vector as int64 first (with overflow check)
  so integer sums >= 1e16 stay exact and parseable instead of
  rendering in scientific notation.
- Use original.size() rather than results_.size() for the Join
  policy in _reduce_results so default_str-derived results join.

Assisted-by: ClaudeCode:claude-opus-4-8
@henryiii henryiii marked this pull request as ready for review June 16, 2026 04:15
@henryiii henryiii merged commit 5c0ba6a into main Jul 1, 2026
73 checks passed
@henryiii henryiii deleted the fix/typetools-conversions 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