fixed date parsing when the year has a leading zero (closes #130)

also:
- fixed omitting value part from hex/bin/oct being accepted without error (closes #129)
- added spec bug github template
This commit is contained in:
Mark Gillard 2022-01-05 10:58:24 +02:00
parent b41e12f736
commit de2413e0ef
11 changed files with 125 additions and 25 deletions

View File

@ -1,8 +1,8 @@
---
name: Bug report
about: Found a bug? Help me squash it.
about: Regular ol' bugs.
title: ''
labels: bug
labels: [ "bug" ]
assignees: marzer
---

View File

@ -0,0 +1,55 @@
---
name: TOML spec conformance bug
about: Bugs relating to the library's TOML spec conformance (or lack thereof).
title: ''
labels: [ "bug", "TOML spec" ]
assignees: marzer
---
<!--
Replace the HTML/TOML comments below with the requested information.
Please don't delete this template and roll your own!
Thanks for contributing!
-->
## The non-conforming TOML snippet
```toml
# your TOML here
```
## What you expected
<!--
e.g. a link to, or snippet from, the TOML spec, or some reasonable description thereof
-->
## What you got
<!--
e.g toml-test output
-->
## Environment
**toml++ version and/or commit hash:**
<!--
If you're using the single-header version of the library, the version number is right at the top of the file.
Otherwise you can find it by opening toml++/impl/version.h; it'll be represented by three defines -
TOML_LIB_MAJOR, TOML_LIB_MINOR and TOML_LIB_PATCH.
If you're not using any particular release and are instead just living large at HEAD of master, the commit hash
would be super helpful too, though it's not critical.
-->
**Any other useful information:**
<!--
Anything else you think will help me fix the issue. Since this report is for general spec conformance handling
you probably don't need to worry about compiler versions, compilation flags, et cetera, though include them if
you feel they're relevant.
-->

View File

@ -35,9 +35,8 @@ code changes at callsites or in build systems are indicated with ⚠&#xFE0F;.
- fixed missing `TOML_API` on interfaces
- fixed parser not correctly round-tripping the format of binary and octal integers in some cases
- fixed strong exception guarantee edge-cases in `toml::table` and `toml::array`
- fixed some incorrect unicode scalar sequence transformations (#125) (@moorereason)
- fixed extended-precision fractional times causing parse error instead of truncating per the spec (#127) (@moorereason)
- fixed some non-spec vertical whitespace being accepted as line breaks (#128) (@moorereason)
- fixed some incorrect unicode scalar sequence transformations (#125)
- fixed a number of spec conformance issues (#127, #128, #129) (@moorereason)
#### Additions:
- added `operator->` to `toml::value` for class types

View File

@ -23,7 +23,7 @@
TOML_IMPL_NAMESPACE_START
{
enum class formatted_string_traits : unsigned
enum class TOML_CLOSED_FLAGS_ENUM formatted_string_traits : unsigned
{
none,
line_breaks = 1u << 0, // \n

View File

@ -264,7 +264,7 @@ TOML_NAMESPACE_START // abi namespace
}
/// \brief Metadata associated with TOML values.
enum class TOML_OPEN_FLAGS_ENUM value_flags : uint16_t
enum class TOML_OPEN_FLAGS_ENUM value_flags : uint16_t // being an "OPEN" flags enum is not an error
{
/// \brief None.
none,

View File

@ -2088,6 +2088,9 @@ TOML_IMPL_NAMESPACE_START
if (*cp != traits::prefix_codepoint)
set_error_and_return_default("expected '"sv, traits::prefix, "', saw '"sv, to_sv(*cp), "'"sv);
advance_and_return_if_error_or_eof({});
if (!traits::is_digit(*cp))
set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv);
}
// consume value chars
@ -2400,7 +2403,6 @@ TOML_IMPL_NAMESPACE_START
node_ptr parse_inline_table();
TOML_NODISCARD
TOML_NEVER_INLINE
node_ptr parse_value_known_prefixes()
{
return_if_error({});
@ -2505,8 +2507,8 @@ TOML_IMPL_NAMESPACE_START
begins_zero = 1 << 14,
signs_msk = has_plus | has_minus,
bzero_msk = begins_zero | has_digits,
bdigit_msk = begins_digit | has_digits,
bdigit_msk = has_digits | begins_digit,
bzero_msk = bdigit_msk | begins_zero,
};
value_traits traits = has_nothing;
const auto has_any = [&](auto t) noexcept { return (traits & t) != has_nothing; };
@ -2516,7 +2518,11 @@ TOML_IMPL_NAMESPACE_START
// examine the first character to get the 'begins with' traits
// (good fail-fast opportunity; all the remaining types begin with numeric digits or signs)
if (is_decimal_digit(*cp))
add_trait(*cp == U'0' ? begins_zero : begins_digit);
{
add_trait(begins_digit);
if (*cp == U'0')
add_trait(begins_zero);
}
else if (is_match(*cp, U'+', U'-'))
add_trait(begins_sign);
else
@ -2601,8 +2607,12 @@ TOML_IMPL_NAMESPACE_START
return_if_error({});
// force further scanning if this could have been a date-time with a space instead of a T
if (char_count == 10u && traits == (bdigit_msk | has_minus) && chars[4] == U'-' && chars[7] == U'-'
&& !is_eof() && *cp == U' ')
if (char_count == 10u //
&& (traits | begins_zero) == (bzero_msk | has_minus) //
&& chars[4] == U'-' //
&& chars[7] == U'-' //
&& !is_eof() //
&& *cp == U' ')
{
const auto pre_advance_count = advance_count;
const auto pre_scan_traits = traits;
@ -2646,7 +2656,7 @@ TOML_IMPL_NAMESPACE_START
// the only valid value type is an integer.
if (char_count == 1u)
{
if (has_any(begins_zero | begins_digit))
if (has_any(begins_digit))
{
val.reset(new value{ static_cast<int64_t>(chars[0] - U'0') });
advance(); // skip the digit
@ -2692,7 +2702,7 @@ TOML_IMPL_NAMESPACE_START
val.reset(new value{ i });
val->ref_cast<int64_t>().flags(flags);
}
else if (has_any(has_e) || (has_any(begins_zero | begins_digit) && chars[1] == U'.'))
else if (has_any(has_e) || (has_any(begins_digit) && chars[1] == U'.'))
val.reset(new value{ parse_float() });
else if (has_any(begins_sign))
{

View File

@ -152,6 +152,11 @@ TEST_CASE("parsing - integers (hex, bin, oct)")
parsing_should_fail(FILE_LINE_ARGS, "val = 0o1000000000000000000000"sv);
parsing_should_fail(FILE_LINE_ARGS, "val = 0b1000000000000000000000000000000000000000000000000000000000000000"sv);
// missing values after base prefix
parsing_should_fail(FILE_LINE_ARGS, "val = 0x "sv);
parsing_should_fail(FILE_LINE_ARGS, "val = 0o "sv);
parsing_should_fail(FILE_LINE_ARGS, "val = 0b "sv);
// value tests
parse_expected_value(FILE_LINE_ARGS, "0xDEADBEEF"sv, 0xDEADBEEF);
parse_expected_value(FILE_LINE_ARGS, "0xdeadbeef"sv, 0xDEADBEEF);

View File

@ -230,4 +230,21 @@ b = []
parsing_should_succeed(FILE_LINE_ARGS, "\t"sv);
parsing_should_succeed(FILE_LINE_ARGS, "\n"sv);
}
SECTION("github/issues/129") // https://github.com/marzer/tomlplusplus/issues/129
{
parsing_should_fail(FILE_LINE_ARGS, R"(
hex = 0x
oct = 0o
bin = 0b
)"sv);
}
SECTION("github/issues/130") // https://github.com/marzer/tomlplusplus/issues/130
{
parse_expected_value(FILE_LINE_ARGS, "0400-01-01 00:00:00"sv, toml::date_time{ { 400, 1, 1 }, { 0, 0, 0 } });
parse_expected_value(FILE_LINE_ARGS, "0400-01-01 "sv, toml::date{ 400, 1, 1 });
parse_expected_value(FILE_LINE_ARGS, "0400-01-01T00:00:00"sv, toml::date_time{ { 400, 1, 1 }, { 0, 0, 0 } });
parse_expected_value(FILE_LINE_ARGS, "1000-01-01 00:00:00"sv, toml::date_time{ { 1000, 1, 1 }, { 0, 0, 0 } });
}
}

View File

@ -89,6 +89,7 @@
<None Include=".github\ISSUE_TEMPLATE\bug_report.md" />
<None Include=".github\ISSUE_TEMPLATE\config.yml" />
<None Include=".github\ISSUE_TEMPLATE\feature_request.md" />
<None Include=".github\ISSUE_TEMPLATE\spec_bug_report.md" />
<None Include=".github\pull_request_template.md" />
<None Include=".gitignore" />
<None Include=".runsettings" />

View File

@ -217,6 +217,9 @@
<None Include="include\toml++\impl\unicode.inl">
<Filter>include\impl</Filter>
</None>
<None Include=".github\ISSUE_TEMPLATE\spec_bug_report.md">
<Filter>.github</Filter>
</None>
</ItemGroup>
<ItemGroup>
<Filter Include=".circleci">

View File

@ -1242,7 +1242,7 @@ TOML_NAMESPACE_START // abi namespace
}
}
enum class TOML_OPEN_FLAGS_ENUM value_flags : uint16_t
enum class TOML_OPEN_FLAGS_ENUM value_flags : uint16_t // being an "OPEN" flags enum is not an error
{
none,
format_as_binary = 1,
@ -12698,6 +12698,9 @@ TOML_IMPL_NAMESPACE_START
if (*cp != traits::prefix_codepoint)
set_error_and_return_default("expected '"sv, traits::prefix, "', saw '"sv, to_sv(*cp), "'"sv);
advance_and_return_if_error_or_eof({});
if (!traits::is_digit(*cp))
set_error_and_return_default("expected digit, saw '"sv, to_sv(*cp), "'"sv);
}
// consume value chars
@ -13010,7 +13013,6 @@ TOML_IMPL_NAMESPACE_START
node_ptr parse_inline_table();
TOML_NODISCARD
TOML_NEVER_INLINE
node_ptr parse_value_known_prefixes()
{
return_if_error({});
@ -13114,8 +13116,8 @@ TOML_IMPL_NAMESPACE_START
begins_digit = 1 << 13,
begins_zero = 1 << 14,
signs_msk = has_plus | has_minus,
bzero_msk = begins_zero | has_digits,
bdigit_msk = begins_digit | has_digits,
bdigit_msk = has_digits | begins_digit,
bzero_msk = bdigit_msk | begins_zero,
};
value_traits traits = has_nothing;
const auto has_any = [&](auto t) noexcept { return (traits & t) != has_nothing; };
@ -13125,7 +13127,11 @@ TOML_IMPL_NAMESPACE_START
// examine the first character to get the 'begins with' traits
// (good fail-fast opportunity; all the remaining types begin with numeric digits or signs)
if (is_decimal_digit(*cp))
add_trait(*cp == U'0' ? begins_zero : begins_digit);
{
add_trait(begins_digit);
if (*cp == U'0')
add_trait(begins_zero);
}
else if (is_match(*cp, U'+', U'-'))
add_trait(begins_sign);
else
@ -13210,8 +13216,12 @@ TOML_IMPL_NAMESPACE_START
return_if_error({});
// force further scanning if this could have been a date-time with a space instead of a T
if (char_count == 10u && traits == (bdigit_msk | has_minus) && chars[4] == U'-' && chars[7] == U'-'
&& !is_eof() && *cp == U' ')
if (char_count == 10u //
&& (traits | begins_zero) == (bzero_msk | has_minus) //
&& chars[4] == U'-' //
&& chars[7] == U'-' //
&& !is_eof() //
&& *cp == U' ')
{
const auto pre_advance_count = advance_count;
const auto pre_scan_traits = traits;
@ -13255,7 +13265,7 @@ TOML_IMPL_NAMESPACE_START
// the only valid value type is an integer.
if (char_count == 1u)
{
if (has_any(begins_zero | begins_digit))
if (has_any(begins_digit))
{
val.reset(new value{ static_cast<int64_t>(chars[0] - U'0') });
advance(); // skip the digit
@ -13301,7 +13311,7 @@ TOML_IMPL_NAMESPACE_START
val.reset(new value{ i });
val->ref_cast<int64_t>().flags(flags);
}
else if (has_any(has_e) || (has_any(begins_zero | begins_digit) && chars[1] == U'.'))
else if (has_any(has_e) || (has_any(begins_digit) && chars[1] == U'.'))
val.reset(new value{ parse_float() });
else if (has_any(begins_sign))
{
@ -14403,7 +14413,7 @@ TOML_PUSH_WARNINGS;
TOML_IMPL_NAMESPACE_START
{
enum class formatted_string_traits : unsigned
enum class TOML_CLOSED_FLAGS_ENUM formatted_string_traits : unsigned
{
none,
line_breaks = 1u << 0, // \n