Skip to content

Commit 2914ba1

Browse files
authored
[libc++] Improve diagnostic when failing to parse the tzdb (#122125)
Providing the character that we failed on is helpful for figuring out what's going wrong in the tzdb.
1 parent 657fb44 commit 2914ba1

File tree

3 files changed

+28
-12
lines changed

3 files changed

+28
-12
lines changed

libcxx/src/experimental/tzdb.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
1010

11+
#include <__assert>
1112
#include <algorithm>
1213
#include <cctype>
1314
#include <chrono>
@@ -97,14 +98,23 @@ static void __skip(istream& __input, string_view __suffix) {
9798
}
9899

99100
static void __matches(istream& __input, char __expected) {
100-
if (std::tolower(__input.get()) != __expected)
101-
std::__throw_runtime_error((string("corrupt tzdb: expected character '") + __expected + '\'').c_str());
101+
_LIBCPP_ASSERT_INTERNAL(!std::isalpha(__expected) || std::islower(__expected), "lowercase characters only here!");
102+
char __c = __input.get();
103+
if (std::tolower(__c) != __expected)
104+
std::__throw_runtime_error(
105+
(string("corrupt tzdb: expected character '") + __expected + "', got '" + __c + "' instead").c_str());
102106
}
103107

104108
static void __matches(istream& __input, string_view __expected) {
105-
for (auto __c : __expected)
106-
if (std::tolower(__input.get()) != __c)
107-
std::__throw_runtime_error((string("corrupt tzdb: expected string '") + string(__expected) + '\'').c_str());
109+
for (auto __c : __expected) {
110+
_LIBCPP_ASSERT_INTERNAL(!std::isalpha(__c) || std::islower(__c), "lowercase strings only here!");
111+
char __actual = __input.get();
112+
if (std::tolower(__actual) != __c)
113+
std::__throw_runtime_error(
114+
(string("corrupt tzdb: expected character '") + __c + "' from string '" + string(__expected) + "', got '" +
115+
__actual + "' instead")
116+
.c_str());
117+
}
108118
}
109119

110120
[[nodiscard]] static string __parse_string(istream& __input) {

libcxx/test/libcxx/time/time.zone/time.zone.db/rules.pass.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@
2020
// ADDITIONAL_COMPILE_FLAGS: -I %{libcxx-dir}/src/experimental/include
2121

2222
#include <chrono>
23+
#include <cstdio>
2324
#include <fstream>
24-
#include <string>
2525
#include <string_view>
26+
#include <string>
2627
#include <variant>
2728

2829
#include "assert_macros.h"
@@ -96,7 +97,7 @@ static void test_invalid() {
9697
test_exception("R r 0 mix", "corrupt tzdb: expected whitespace");
9798
test_exception("R r 0 1", "corrupt tzdb: expected whitespace");
9899

99-
test_exception("R r 0 1 X", "corrupt tzdb: expected character '-'");
100+
test_exception("R r 0 1 X", "corrupt tzdb: expected character '-', got 'X' instead");
100101

101102
test_exception("R r 0 1 -", "corrupt tzdb: expected whitespace");
102103

@@ -106,13 +107,17 @@ static void test_invalid() {
106107

107108
test_exception("R r 0 1 - Ja +", "corrupt tzdb weekday: invalid name");
108109
test_exception("R r 0 1 - Ja 32", "corrupt tzdb day: value too large");
109-
test_exception("R r 0 1 - Ja l", "corrupt tzdb: expected string 'last'");
110+
test_exception(
111+
"R r 0 1 - Ja l",
112+
std::string{"corrupt tzdb: expected character 'a' from string 'last', got '"} + (char)EOF + "' instead");
110113
test_exception("R r 0 1 - Ja last", "corrupt tzdb weekday: invalid name");
111114
test_exception("R r 0 1 - Ja lastS", "corrupt tzdb weekday: invalid name");
112115
test_exception("R r 0 1 - Ja S", "corrupt tzdb weekday: invalid name");
113116
test_exception("R r 0 1 - Ja Su", "corrupt tzdb on: expected '>=' or '<='");
114-
test_exception("R r 0 1 - Ja Su>", "corrupt tzdb: expected character '='");
115-
test_exception("R r 0 1 - Ja Su<", "corrupt tzdb: expected character '='");
117+
test_exception(
118+
"R r 0 1 - Ja Su>", std::string{"corrupt tzdb: expected character '=', got '"} + (char)EOF + "' instead");
119+
test_exception(
120+
"R r 0 1 - Ja Su<", std::string{"corrupt tzdb: expected character '=', got '"} + (char)EOF + "' instead");
116121
test_exception("R r 0 1 - Ja Su>=+", "corrupt tzdb: expected a non-zero digit");
117122
test_exception("R r 0 1 - Ja Su>=0", "corrupt tzdb: expected a non-zero digit");
118123
test_exception("R r 0 1 - Ja Su>=32", "corrupt tzdb day: value too large");

libcxx/test/libcxx/time/time.zone/time.zone.db/version.pass.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
// This is not part of the public tzdb interface.
1919

2020
#include <chrono>
21+
#include <cstdio>
2122
#include <fstream>
22-
#include <string>
2323
#include <string_view>
24+
#include <string>
2425

2526
#include "assert_macros.h"
2627
#include "concat_macros.h"
@@ -60,7 +61,7 @@ static void test_exception(std::string_view input, [[maybe_unused]] std::string_
6061
}
6162

6263
int main(int, const char**) {
63-
test_exception("", "corrupt tzdb: expected character '#'");
64+
test_exception("", std::string{"corrupt tzdb: expected character '#', got '"} + (char)EOF + "' instead");
6465
test_exception("#version", "corrupt tzdb: expected whitespace");
6566
test("#version \t ABCD", "ABCD");
6667
test("#Version \t ABCD", "ABCD");

0 commit comments

Comments
 (0)