Skip to content

Commit

Permalink
[libc++] Improve diagnostic when failing to parse the tzdb (llvm#122125)
Browse files Browse the repository at this point in the history
Providing the character that we failed on is helpful for figuring out
what's going wrong in the tzdb.
  • Loading branch information
ldionne authored Jan 11, 2025
1 parent 657fb44 commit 2914ba1
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 12 deletions.
20 changes: 15 additions & 5 deletions libcxx/src/experimental/tzdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

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

#include <__assert>
#include <algorithm>
#include <cctype>
#include <chrono>
Expand Down Expand Up @@ -97,14 +98,23 @@ static void __skip(istream& __input, string_view __suffix) {
}

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

static void __matches(istream& __input, string_view __expected) {
for (auto __c : __expected)
if (std::tolower(__input.get()) != __c)
std::__throw_runtime_error((string("corrupt tzdb: expected string '") + string(__expected) + '\'').c_str());
for (auto __c : __expected) {
_LIBCPP_ASSERT_INTERNAL(!std::isalpha(__c) || std::islower(__c), "lowercase strings only here!");
char __actual = __input.get();
if (std::tolower(__actual) != __c)
std::__throw_runtime_error(
(string("corrupt tzdb: expected character '") + __c + "' from string '" + string(__expected) + "', got '" +
__actual + "' instead")
.c_str());
}
}

[[nodiscard]] static string __parse_string(istream& __input) {
Expand Down
15 changes: 10 additions & 5 deletions libcxx/test/libcxx/time/time.zone/time.zone.db/rules.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
// ADDITIONAL_COMPILE_FLAGS: -I %{libcxx-dir}/src/experimental/include

#include <chrono>
#include <cstdio>
#include <fstream>
#include <string>
#include <string_view>
#include <string>
#include <variant>

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

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

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

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

test_exception("R r 0 1 - Ja +", "corrupt tzdb weekday: invalid name");
test_exception("R r 0 1 - Ja 32", "corrupt tzdb day: value too large");
test_exception("R r 0 1 - Ja l", "corrupt tzdb: expected string 'last'");
test_exception(
"R r 0 1 - Ja l",
std::string{"corrupt tzdb: expected character 'a' from string 'last', got '"} + (char)EOF + "' instead");
test_exception("R r 0 1 - Ja last", "corrupt tzdb weekday: invalid name");
test_exception("R r 0 1 - Ja lastS", "corrupt tzdb weekday: invalid name");
test_exception("R r 0 1 - Ja S", "corrupt tzdb weekday: invalid name");
test_exception("R r 0 1 - Ja Su", "corrupt tzdb on: expected '>=' or '<='");
test_exception("R r 0 1 - Ja Su>", "corrupt tzdb: expected character '='");
test_exception("R r 0 1 - Ja Su<", "corrupt tzdb: expected character '='");
test_exception(
"R r 0 1 - Ja Su>", std::string{"corrupt tzdb: expected character '=', got '"} + (char)EOF + "' instead");
test_exception(
"R r 0 1 - Ja Su<", std::string{"corrupt tzdb: expected character '=', got '"} + (char)EOF + "' instead");
test_exception("R r 0 1 - Ja Su>=+", "corrupt tzdb: expected a non-zero digit");
test_exception("R r 0 1 - Ja Su>=0", "corrupt tzdb: expected a non-zero digit");
test_exception("R r 0 1 - Ja Su>=32", "corrupt tzdb day: value too large");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
// This is not part of the public tzdb interface.

#include <chrono>
#include <cstdio>
#include <fstream>
#include <string>
#include <string_view>
#include <string>

#include "assert_macros.h"
#include "concat_macros.h"
Expand Down Expand Up @@ -60,7 +61,7 @@ static void test_exception(std::string_view input, [[maybe_unused]] std::string_
}

int main(int, const char**) {
test_exception("", "corrupt tzdb: expected character '#'");
test_exception("", std::string{"corrupt tzdb: expected character '#', got '"} + (char)EOF + "' instead");
test_exception("#version", "corrupt tzdb: expected whitespace");
test("#version \t ABCD", "ABCD");
test("#Version \t ABCD", "ABCD");
Expand Down

0 comments on commit 2914ba1

Please sign in to comment.