From 60b6066febe67eb58d9444c8137b0e820729a088 Mon Sep 17 00:00:00 2001 From: Derek Bailey Date: Fri, 26 Jun 2020 11:16:02 -0700 Subject: [PATCH] Add warning to schema parser if field name is not snake_case. (#6005) * Added code gen for evolution tests back in. * General generate code and clang format * Added code gen for evolution tests back in. * General generate code and clang format * reran generate_code.sh * Added code gen for evolution tests back in. * General generate code and clang format * Added code gen for evolution tests back in. * General generate code and clang format * Enforce snake_case for schema field names * Switched to basic for loop, Fixed warning message --- src/idl_parser.cpp | 14 ++++ tests/test.cpp | 172 +++++++++++++++++++++++---------------------- 2 files changed, 101 insertions(+), 85 deletions(-) diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 897bf8e78..6faca7acd 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -82,6 +82,16 @@ static bool ValidateUTF8(const std::string &str) { return true; } +static bool IsLowerSnakeCase(const std::string &str) { + for (size_t i = 0; i < str.length(); i++) { + char c = str[i]; + if (!check_ascii_range(c, 'a', 'z') && !is_digit(c) && c != '_') { + return false; + } + } + return true; +} + // Convert an underscore_based_indentifier in to camelCase. // Also uppercases the first character if first is true. std::string MakeCamel(const std::string &in, bool first) { @@ -671,6 +681,10 @@ CheckedError Parser::ParseField(StructDef &struct_def) { if (LookupCreateStruct(name, false, false)) return Error("field name can not be the same as table/struct name"); + if (!IsLowerSnakeCase(name)) { + Warning("field names should be lowercase snake_case, got: " + name); + } + std::vector dc = doc_comment_; EXPECT(kTokenIdentifier); EXPECT(':'); diff --git a/tests/test.cpp b/tests/test.cpp index 469687a52..b7ae76726 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -1549,7 +1549,7 @@ void TestError_(const char *src, const char *error_substr, bool strict_json, ("parser.Parse(\"" + std::string(src) + "\")").c_str(), file, line, func); } else if (!strstr(parser.error_.c_str(), error_substr)) { - TestFail(parser.error_.c_str(), error_substr, + TestFail(error_substr, parser.error_.c_str(), ("parser.Parse(\"" + std::string(src) + "\")").c_str(), file, line, func); } @@ -1626,6 +1626,8 @@ void ErrorTest() { // Array of non-scalar TestError("table X { x:int; } struct Y { y:[X:2]; }", "may contain only scalar or struct fields"); + // Non-snake case field names + TestError("table X { Y: int; } root_type Y: {Y:1.0}", "snake_case"); } template @@ -1637,7 +1639,7 @@ T TestValue(const char *json, const char *type_name, if (check_default) { parser.opts.output_default_scalars_in_json = true; } // Simple schema. std::string schema = std::string(decls ? decls : "") + "\n" + - "table X { Y:" + std::string(type_name) + + "table X { y:" + std::string(type_name) + "; } root_type X;"; auto schema_done = parser.Parse(schema.c_str()); TEST_EQ_STR(parser.error_.c_str(), ""); @@ -1666,45 +1668,45 @@ bool FloatCompare(float a, float b) { return fabs(a - b) < 0.001; } void ValueTest() { // Test scientific notation numbers. TEST_EQ( - FloatCompare(TestValue("{ Y:0.0314159e+2 }", "float"), 3.14159f), + FloatCompare(TestValue("{ y:0.0314159e+2 }", "float"), 3.14159f), true); // number in string - TEST_EQ(FloatCompare(TestValue("{ Y:\"0.0314159e+2\" }", "float"), + TEST_EQ(FloatCompare(TestValue("{ y:\"0.0314159e+2\" }", "float"), 3.14159f), true); // Test conversion functions. - TEST_EQ(FloatCompare(TestValue("{ Y:cos(rad(180)) }", "float"), -1), + TEST_EQ(FloatCompare(TestValue("{ y:cos(rad(180)) }", "float"), -1), true); // int embedded to string - TEST_EQ(TestValue("{ Y:\"-876\" }", "int=-123"), -876); - TEST_EQ(TestValue("{ Y:\"876\" }", "int=-123"), 876); + TEST_EQ(TestValue("{ y:\"-876\" }", "int=-123"), -876); + TEST_EQ(TestValue("{ y:\"876\" }", "int=-123"), 876); // Test negative hex constant. - TEST_EQ(TestValue("{ Y:-0x8ea0 }", "int=-0x8ea0"), -36512); + TEST_EQ(TestValue("{ y:-0x8ea0 }", "int=-0x8ea0"), -36512); TEST_EQ(TestValue(nullptr, "int=-0x8ea0"), -36512); // positive hex constant - TEST_EQ(TestValue("{ Y:0x1abcdef }", "int=0x1"), 0x1abcdef); + TEST_EQ(TestValue("{ y:0x1abcdef }", "int=0x1"), 0x1abcdef); // with optional '+' sign - TEST_EQ(TestValue("{ Y:+0x1abcdef }", "int=+0x1"), 0x1abcdef); + TEST_EQ(TestValue("{ y:+0x1abcdef }", "int=+0x1"), 0x1abcdef); // hex in string - TEST_EQ(TestValue("{ Y:\"0x1abcdef\" }", "int=+0x1"), 0x1abcdef); + TEST_EQ(TestValue("{ y:\"0x1abcdef\" }", "int=+0x1"), 0x1abcdef); // Make sure we do unsigned 64bit correctly. - TEST_EQ(TestValue("{ Y:12335089644688340133 }", "ulong"), + TEST_EQ(TestValue("{ y:12335089644688340133 }", "ulong"), 12335089644688340133ULL); // bool in string - TEST_EQ(TestValue("{ Y:\"false\" }", "bool=true"), false); - TEST_EQ(TestValue("{ Y:\"true\" }", "bool=\"true\""), true); - TEST_EQ(TestValue("{ Y:'false' }", "bool=true"), false); - TEST_EQ(TestValue("{ Y:'true' }", "bool=\"true\""), true); + TEST_EQ(TestValue("{ y:\"false\" }", "bool=true"), false); + TEST_EQ(TestValue("{ y:\"true\" }", "bool=\"true\""), true); + TEST_EQ(TestValue("{ y:'false' }", "bool=true"), false); + TEST_EQ(TestValue("{ y:'true' }", "bool=\"true\""), true); // check comments before and after json object - TEST_EQ(TestValue("/*before*/ { Y:1 } /*after*/", "int"), 1); - TEST_EQ(TestValue("//before \n { Y:1 } //after", "int"), 1); + TEST_EQ(TestValue("/*before*/ { y:1 } /*after*/", "int"), 1); + TEST_EQ(TestValue("//before \n { y:1 } //after", "int"), 1); } void NestedListTest() { @@ -1776,18 +1778,18 @@ void EnumOutOfRangeTest() { } void EnumValueTest() { - // json: "{ Y:0 }", schema: table X { Y : "E"} + // json: "{ Y:0 }", schema: table X { y: "E"} // 0 in enum (V=0) E then Y=0 is valid. - TEST_EQ(TestValue("{ Y:0 }", "E", "enum E:int { V }"), 0); - TEST_EQ(TestValue("{ Y:V }", "E", "enum E:int { V }"), 0); + TEST_EQ(TestValue("{ y:0 }", "E", "enum E:int { V }"), 0); + TEST_EQ(TestValue("{ y:V }", "E", "enum E:int { V }"), 0); // A default value of Y is 0. TEST_EQ(TestValue("{ }", "E", "enum E:int { V }"), 0); - TEST_EQ(TestValue("{ Y:5 }", "E=V", "enum E:int { V=5 }"), 5); + TEST_EQ(TestValue("{ y:5 }", "E=V", "enum E:int { V=5 }"), 5); // Generate json with defaults and check. TEST_EQ(TestValue(nullptr, "E=V", "enum E:int { V=5 }"), 5); // 5 in enum - TEST_EQ(TestValue("{ Y:5 }", "E", "enum E:int { Z, V=5 }"), 5); - TEST_EQ(TestValue("{ Y:5 }", "E=V", "enum E:int { Z, V=5 }"), 5); + TEST_EQ(TestValue("{ y:5 }", "E", "enum E:int { Z, V=5 }"), 5); + TEST_EQ(TestValue("{ y:5 }", "E=V", "enum E:int { Z, V=5 }"), 5); // Generate json with defaults and check. TEST_EQ(TestValue(nullptr, "E", "enum E:int { Z, V=5 }"), 0); TEST_EQ(TestValue(nullptr, "E=V", "enum E:int { Z, V=5 }"), 5); @@ -1799,9 +1801,9 @@ void EnumValueTest() { "enum E:ulong { V = 18446744073709551615 }"), 18446744073709551615ULL); // Assign non-enum value to enum field. Is it right? - TEST_EQ(TestValue("{ Y:7 }", "E", "enum E:int { V = 0 }"), 7); + TEST_EQ(TestValue("{ y:7 }", "E", "enum E:int { V = 0 }"), 7); // Check that non-ascending values are valid. - TEST_EQ(TestValue("{ Y:5 }", "E=V", "enum E:int { Z=10, V=5 }"), 5); + TEST_EQ(TestValue("{ y:5 }", "E=V", "enum E:int { Z=10, V=5 }"), 5); } void IntegerOutOfRangeTest() { @@ -1895,26 +1897,26 @@ void IntegerBoundaryTest() { TEST_EQ(flatbuffers::numeric_limits::max(), 18446744073709551615ULL); - TEST_EQ(TestValue("{ Y:127 }", "byte"), 127); - TEST_EQ(TestValue("{ Y:-128 }", "byte"), -128); - TEST_EQ(TestValue("{ Y:255 }", "ubyte"), 255); - TEST_EQ(TestValue("{ Y:0 }", "ubyte"), 0); - TEST_EQ(TestValue("{ Y:32767 }", "short"), 32767); - TEST_EQ(TestValue("{ Y:-32768 }", "short"), -32768); - TEST_EQ(TestValue("{ Y:65535 }", "ushort"), 65535); - TEST_EQ(TestValue("{ Y:0 }", "ushort"), 0); - TEST_EQ(TestValue("{ Y:2147483647 }", "int"), 2147483647); - TEST_EQ(TestValue("{ Y:-2147483648 }", "int") + 1, -2147483647); - TEST_EQ(TestValue("{ Y:4294967295 }", "uint"), 4294967295); - TEST_EQ(TestValue("{ Y:0 }", "uint"), 0); - TEST_EQ(TestValue("{ Y:9223372036854775807 }", "long"), + TEST_EQ(TestValue("{ y:127 }", "byte"), 127); + TEST_EQ(TestValue("{ y:-128 }", "byte"), -128); + TEST_EQ(TestValue("{ y:255 }", "ubyte"), 255); + TEST_EQ(TestValue("{ y:0 }", "ubyte"), 0); + TEST_EQ(TestValue("{ y:32767 }", "short"), 32767); + TEST_EQ(TestValue("{ y:-32768 }", "short"), -32768); + TEST_EQ(TestValue("{ y:65535 }", "ushort"), 65535); + TEST_EQ(TestValue("{ y:0 }", "ushort"), 0); + TEST_EQ(TestValue("{ y:2147483647 }", "int"), 2147483647); + TEST_EQ(TestValue("{ y:-2147483648 }", "int") + 1, -2147483647); + TEST_EQ(TestValue("{ y:4294967295 }", "uint"), 4294967295); + TEST_EQ(TestValue("{ y:0 }", "uint"), 0); + TEST_EQ(TestValue("{ y:9223372036854775807 }", "long"), 9223372036854775807LL); - TEST_EQ(TestValue("{ Y:-9223372036854775808 }", "long") + 1LL, + TEST_EQ(TestValue("{ y:-9223372036854775808 }", "long") + 1LL, -9223372036854775807LL); - TEST_EQ(TestValue("{ Y:18446744073709551615 }", "ulong"), + TEST_EQ(TestValue("{ y:18446744073709551615 }", "ulong"), 18446744073709551615ULL); - TEST_EQ(TestValue("{ Y:0 }", "ulong"), 0); - TEST_EQ(TestValue("{ Y: 18446744073709551615 }", "uint64"), + TEST_EQ(TestValue("{ y:0 }", "ulong"), 0); + TEST_EQ(TestValue("{ y: 18446744073709551615 }", "uint64"), 18446744073709551615ULL); // check that the default works TEST_EQ(TestValue(nullptr, "uint64 = 18446744073709551615"), @@ -1923,77 +1925,77 @@ void IntegerBoundaryTest() { void ValidFloatTest() { // check rounding to infinity - TEST_EQ(TestValue("{ Y:+3.4029e+38 }", "float"), +infinityf); - TEST_EQ(TestValue("{ Y:-3.4029e+38 }", "float"), -infinityf); - TEST_EQ(TestValue("{ Y:+1.7977e+308 }", "double"), +infinityd); - TEST_EQ(TestValue("{ Y:-1.7977e+308 }", "double"), -infinityd); + TEST_EQ(TestValue("{ y:+3.4029e+38 }", "float"), +infinityf); + TEST_EQ(TestValue("{ y:-3.4029e+38 }", "float"), -infinityf); + TEST_EQ(TestValue("{ y:+1.7977e+308 }", "double"), +infinityd); + TEST_EQ(TestValue("{ y:-1.7977e+308 }", "double"), -infinityd); TEST_EQ( - FloatCompare(TestValue("{ Y:0.0314159e+2 }", "float"), 3.14159f), + FloatCompare(TestValue("{ y:0.0314159e+2 }", "float"), 3.14159f), true); // float in string - TEST_EQ(FloatCompare(TestValue("{ Y:\" 0.0314159e+2 \" }", "float"), + TEST_EQ(FloatCompare(TestValue("{ y:\" 0.0314159e+2 \" }", "float"), 3.14159f), true); - TEST_EQ(TestValue("{ Y:1 }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:1.0 }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:1. }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:+1. }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:-1. }", "float"), -1.0f); - TEST_EQ(TestValue("{ Y:1.e0 }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:1.e+0 }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:1.e-0 }", "float"), 1.0f); - TEST_EQ(TestValue("{ Y:0.125 }", "float"), 0.125f); - TEST_EQ(TestValue("{ Y:.125 }", "float"), 0.125f); - TEST_EQ(TestValue("{ Y:-.125 }", "float"), -0.125f); - TEST_EQ(TestValue("{ Y:+.125 }", "float"), +0.125f); - TEST_EQ(TestValue("{ Y:5 }", "float"), 5.0f); - TEST_EQ(TestValue("{ Y:\"5\" }", "float"), 5.0f); + TEST_EQ(TestValue("{ y:1 }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:1.0 }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:1. }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:+1. }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:-1. }", "float"), -1.0f); + TEST_EQ(TestValue("{ y:1.e0 }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:1.e+0 }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:1.e-0 }", "float"), 1.0f); + TEST_EQ(TestValue("{ y:0.125 }", "float"), 0.125f); + TEST_EQ(TestValue("{ y:.125 }", "float"), 0.125f); + TEST_EQ(TestValue("{ y:-.125 }", "float"), -0.125f); + TEST_EQ(TestValue("{ y:+.125 }", "float"), +0.125f); + TEST_EQ(TestValue("{ y:5 }", "float"), 5.0f); + TEST_EQ(TestValue("{ y:\"5\" }", "float"), 5.0f); #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0) // Old MSVC versions may have problem with this check. // https://www.exploringbinary.com/visual-c-plus-plus-strtod-still-broken/ - TEST_EQ(TestValue("{ Y:6.9294956446009195e15 }", "double"), + TEST_EQ(TestValue("{ y:6.9294956446009195e15 }", "double"), 6929495644600920.0); // check nan's - TEST_EQ(std::isnan(TestValue("{ Y:nan }", "double")), true); - TEST_EQ(std::isnan(TestValue("{ Y:nan }", "float")), true); - TEST_EQ(std::isnan(TestValue("{ Y:\"nan\" }", "float")), true); - TEST_EQ(std::isnan(TestValue("{ Y:+nan }", "float")), true); - TEST_EQ(std::isnan(TestValue("{ Y:-nan }", "float")), true); + TEST_EQ(std::isnan(TestValue("{ y:nan }", "double")), true); + TEST_EQ(std::isnan(TestValue("{ y:nan }", "float")), true); + TEST_EQ(std::isnan(TestValue("{ y:\"nan\" }", "float")), true); + TEST_EQ(std::isnan(TestValue("{ y:+nan }", "float")), true); + TEST_EQ(std::isnan(TestValue("{ y:-nan }", "float")), true); TEST_EQ(std::isnan(TestValue(nullptr, "float=nan")), true); TEST_EQ(std::isnan(TestValue(nullptr, "float=-nan")), true); // check inf - TEST_EQ(TestValue("{ Y:inf }", "float"), infinityf); - TEST_EQ(TestValue("{ Y:\"inf\" }", "float"), infinityf); - TEST_EQ(TestValue("{ Y:+inf }", "float"), infinityf); - TEST_EQ(TestValue("{ Y:-inf }", "float"), -infinityf); + TEST_EQ(TestValue("{ y:inf }", "float"), infinityf); + TEST_EQ(TestValue("{ y:\"inf\" }", "float"), infinityf); + TEST_EQ(TestValue("{ y:+inf }", "float"), infinityf); + TEST_EQ(TestValue("{ y:-inf }", "float"), -infinityf); TEST_EQ(TestValue(nullptr, "float=inf"), infinityf); TEST_EQ(TestValue(nullptr, "float=-inf"), -infinityf); TestValue( - "{ Y : [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, " + "{ y: [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, " "3.0e2] }", "[double]"); TestValue( - "{ Y : [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, " + "{ y: [0.2, .2, 1.0, -1.0, -2., 2., 1e0, -1e0, 1.0e0, -1.0e0, -3.e2, " "3.0e2] }", "[float]"); // Test binary format of float point. // https://en.cppreference.com/w/cpp/language/floating_literal // 0x11.12p-1 = (1*16^1 + 2*16^0 + 3*16^-1 + 4*16^-2) * 2^-1 = - TEST_EQ(TestValue("{ Y:0x12.34p-1 }", "double"), 9.1015625); + TEST_EQ(TestValue("{ y:0x12.34p-1 }", "double"), 9.1015625); // hex fraction 1.2 (decimal 1.125) scaled by 2^3, that is 9.0 - TEST_EQ(TestValue("{ Y:-0x0.2p0 }", "float"), -0.125f); - TEST_EQ(TestValue("{ Y:-0x.2p1 }", "float"), -0.25f); - TEST_EQ(TestValue("{ Y:0x1.2p3 }", "float"), 9.0f); - TEST_EQ(TestValue("{ Y:0x10.1p0 }", "float"), 16.0625f); - TEST_EQ(TestValue("{ Y:0x1.2p3 }", "double"), 9.0); - TEST_EQ(TestValue("{ Y:0x10.1p0 }", "double"), 16.0625); - TEST_EQ(TestValue("{ Y:0xC.68p+2 }", "double"), 49.625); - TestValue("{ Y : [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[double]"); - TestValue("{ Y : [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[float]"); + TEST_EQ(TestValue("{ y:-0x0.2p0 }", "float"), -0.125f); + TEST_EQ(TestValue("{ y:-0x.2p1 }", "float"), -0.25f); + TEST_EQ(TestValue("{ y:0x1.2p3 }", "float"), 9.0f); + TEST_EQ(TestValue("{ y:0x10.1p0 }", "float"), 16.0625f); + TEST_EQ(TestValue("{ y:0x1.2p3 }", "double"), 9.0); + TEST_EQ(TestValue("{ y:0x10.1p0 }", "double"), 16.0625); + TEST_EQ(TestValue("{ y:0xC.68p+2 }", "double"), 49.625); + TestValue("{ y: [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[double]"); + TestValue("{ y: [0x20.4ep1, +0x20.4ep1, -0x20.4ep1] }", "[float]"); #else // FLATBUFFERS_HAS_NEW_STRTOD TEST_OUTPUT_LINE("FLATBUFFERS_HAS_NEW_STRTOD tests skipped");