diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index e0a7a951f..4905d3362 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -93,7 +93,7 @@ parse_i64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i64, } - i := 0 + i, digits := 0, 0 for r in s { if r == '_' { i += 1 @@ -116,9 +116,11 @@ parse_i64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i64, value = value*i64(base) + v } i += 1 + digits += 1 } s = s[i:] - ok = len(s) == 0 + // Underscores are only separators; at least one digit is required. + ok = len(s) == 0 && digits > 0 return } /* @@ -180,7 +182,7 @@ parse_i64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i64, ok: } - i := 0 + i, digits := 0, 0 for r in s { if r == '_' { i += 1 @@ -203,9 +205,11 @@ parse_i64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i64, ok: value = value*base + v } i += 1 + digits += 1 } s = s[i:] - ok = len(s) == 0 + // Underscores are only separators; at least one digit is required. + ok = len(s) == 0 && digits > 0 return } // @@ -252,7 +256,7 @@ parse_u64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u64, s = s[1:] } - i := 0 + i, digits := 0, 0 for r in s { if r == '_' { i += 1 @@ -267,10 +271,12 @@ parse_u64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u64, } value = value*u64(base) + v i += 1 + digits += 1 } s = s[i:] - ok = len(s) == 0 + // Underscores are only separators; at least one digit is required. + ok = len(s) == 0 && digits > 0 return } /* @@ -327,7 +333,7 @@ parse_u64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u64, ok: } } - i := 0 + i, digits := 0, 0 for r in s { if r == '_' { i += 1 @@ -342,10 +348,12 @@ parse_u64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u64, ok: } value = value*base + v i += 1 + digits += 1 } s = s[i:] - ok = len(s) == 0 + // Underscores are only separators; at least one digit is required. + ok = len(s) == 0 && digits > 0 return } // @@ -487,7 +495,7 @@ parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i12 } - i := 0 + i, digits := 0, 0 for r in s { if r == '_' { i += 1 @@ -510,9 +518,11 @@ parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i12 value = value*i128(base) + v } i += 1 + digits += 1 } s = s[i:] - ok = len(s) == 0 + // Underscores are only separators; at least one digit is required. + ok = len(s) == 0 && digits > 0 return } /* @@ -574,7 +584,7 @@ parse_i128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i128, o } - i := 0 + i, digits := 0, 0 for r in s { if r == '_' { i += 1 @@ -597,9 +607,11 @@ parse_i128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i128, o value = value*base + v } i += 1 + digits += 1 } s = s[i:] - ok = len(s) == 0 + // Underscores are only separators; at least one digit is required. + ok = len(s) == 0 && digits > 0 return } // @@ -645,7 +657,7 @@ parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u12 s = s[1:] } - i := 0 + i, digits := 0, 0 for r in s { if r == '_' { i += 1 @@ -660,10 +672,12 @@ parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u12 } value = value*u128(base) + v i += 1 + digits += 1 } s = s[i:] - ok = len(s) == 0 + // Underscores are only separators; at least one digit is required. + ok = len(s) == 0 && digits > 0 return } /* @@ -717,7 +731,7 @@ parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, o } } - i := 0 + i, digits := 0, 0 for r in s { if r == '_' { i += 1 @@ -732,10 +746,12 @@ parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, o } value = value*base + v i += 1 + digits += 1 } s = s[i:] - ok = len(s) == 0 + // Underscores are only separators; at least one digit is required. + ok = len(s) == 0 && digits > 0 return } // diff --git a/tests/core/strconv/test_core_strconv.odin b/tests/core/strconv/test_core_strconv.odin index 5dde6c7b3..482375fa7 100644 --- a/tests/core/strconv/test_core_strconv.odin +++ b/tests/core/strconv/test_core_strconv.odin @@ -268,6 +268,56 @@ test_parse_int_overflow :: proc(t: ^testing.T) { _, ok = strconv.parse_uint("18446744073709551616") testing.expect_value(t, ok, false) } + { + // Cases reported in issue #7608. + _, ok := strconv.parse_u64("36893488147419103232") + testing.expect_value(t, ok, false) + _, ok = strconv.parse_u64("18446744073709551616", 10) + testing.expect_value(t, ok, false) + _, ok = strconv.parse_u64_maybe_prefixed("0xffffffffffffffffff") + testing.expect_value(t, ok, false) + _, ok = strconv.parse_int("99999999999999999999") + testing.expect_value(t, ok, false) + } +} + +@(test) +test_parse_int_underscore_only :: proc(t: ^testing.T) { + for s in ([]string{"_", "__", "+_", "-_", "0x_", "0b__"}) { + _, ok_i64 := strconv.parse_i64(s) + testing.expectf(t, !ok_i64, "parse_i64(%q) should fail", s) + _, ok_u64 := strconv.parse_u64(s) + testing.expectf(t, !ok_u64, "parse_u64(%q) should fail", s) + _, ok_i128 := strconv.parse_i128(s) + testing.expectf(t, !ok_i128, "parse_i128(%q) should fail", s) + _, ok_u128 := strconv.parse_u128(s) + testing.expectf(t, !ok_u128, "parse_u128(%q) should fail", s) + _, ok_int := strconv.parse_int(s) + testing.expectf(t, !ok_int, "parse_int(%q) should fail", s) + _, ok_uint := strconv.parse_uint(s) + testing.expectf(t, !ok_uint, "parse_uint(%q) should fail", s) + } + for s in ([]string{"_", "__", "+_", "-_"}) { + _, ok_i64 := strconv.parse_i64(s, 10) + testing.expectf(t, !ok_i64, "parse_i64(%q, 10) should fail", s) + _, ok_u64 := strconv.parse_u64(s, 10) + testing.expectf(t, !ok_u64, "parse_u64(%q, 10) should fail", s) + _, ok_i128 := strconv.parse_i128(s, 10) + testing.expectf(t, !ok_i128, "parse_i128(%q, 10) should fail", s) + _, ok_u128 := strconv.parse_u128(s, 10) + testing.expectf(t, !ok_u128, "parse_u128(%q, 10) should fail", s) + } + + // Underscores alongside at least one digit are still accepted. + v, ok := strconv.parse_u64("_1_") + testing.expect_value(t, v, 1) + testing.expect_value(t, ok, true) + v, ok = strconv.parse_u64("0x_ff") + testing.expect_value(t, v, 0xff) + testing.expect_value(t, ok, true) + iv, iok := strconv.parse_i64("-1_000", 10) + testing.expect_value(t, iv, -1000) + testing.expect_value(t, iok, true) } test_float_hex :: proc(t: ^testing.T) {