diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index f6384894535..b564b1b2714 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -541,8 +541,11 @@ inline bool operator!=(const EnumVal &lhs, const EnumVal &rhs) { inline bool EqualByName(const Type &a, const Type &b) { return a.base_type == b.base_type && a.element == b.element && (a.struct_def == b.struct_def || - a.struct_def->name == b.struct_def->name) && - (a.enum_def == b.enum_def || a.enum_def->name == b.enum_def->name); + (a.struct_def != nullptr && b.struct_def != nullptr && + a.struct_def->name == b.struct_def->name)) && + (a.enum_def == b.enum_def || + (a.enum_def != nullptr && b.enum_def != nullptr && + a.enum_def->name == b.enum_def->name)); } struct RPCCall : public Definition { diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 854d2a7cfdc..9650cc9dd5b 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -4239,8 +4239,13 @@ std::string Parser::ConformTo(const Parser &base) { field_base = *fbit; if (field.value.offset == field_base->value.offset) { renamed_fields.insert(field_base); - if (!EqualByName(field.value.type, field_base->value.type)) - return "field renamed to different type: " + qualified_field_name; + if (!EqualByName(field.value.type, field_base->value.type)) { + const auto qualified_field_base = + qualified_name + "." + field_base->name; + return "field renamed to different type: " + + qualified_field_name + " (renamed from " + + qualified_field_base + ")"; + } break; } } diff --git a/tests/evolution_test.cpp b/tests/evolution_test.cpp index e7404d4218d..6c23d3fe135 100644 --- a/tests/evolution_test.cpp +++ b/tests/evolution_test.cpp @@ -80,26 +80,37 @@ void EvolutionTest(const std::string &tests_data_path) { #endif } + void ConformTest() { - flatbuffers::Parser parser; - TEST_EQ(parser.Parse("table T { A:int; } enum E:byte { A }"), true); + const char ref[] = "table T { A:int; } enum E:byte { A }"; - auto test_conform = [](flatbuffers::Parser &parser1, const char *test, + auto test_conform = [](const char *ref, const char *test, const char *expected_err) { + flatbuffers::Parser parser1; + TEST_EQ(parser1.Parse(ref), true); flatbuffers::Parser parser2; TEST_EQ(parser2.Parse(test), true); auto err = parser2.ConformTo(parser1); - TEST_NOTNULL(strstr(err.c_str(), expected_err)); + if (*expected_err == '\0') { + TEST_EQ_STR(err.c_str(), expected_err); + } else { + TEST_NOTNULL(strstr(err.c_str(), expected_err)); + } }; - test_conform(parser, "table T { A:byte; }", "types differ for field"); - test_conform(parser, "table T { B:int; A:int; }", "offsets differ for field"); - test_conform(parser, "table T { A:int = 1; }", "defaults differ for field"); - test_conform(parser, "table T { B:float; }", - "field renamed to different type"); - test_conform(parser, "enum E:byte { B, A }", "values differ for enum"); - test_conform(parser, "table T { }", "field deleted"); - test_conform(parser, "table T { B:int; }", ""); //renaming a field is allowed + test_conform(ref, "table T { A:byte; }", "types differ for field: T.A"); + test_conform(ref, "table T { B:int; A:int; }", + "offsets differ for field: T.A"); + test_conform(ref, "table T { A:int = 1; }", "defaults differ for field: T.A"); + test_conform(ref, "table T { B:float; }", + "field renamed to different type: T.B (renamed from T.A)"); + test_conform(ref, "enum E:byte { B, A }", "values differ for enum: A"); + test_conform(ref, "table T { }", "field deleted: T.A"); + test_conform(ref, "table T { B:int; }", ""); // renaming a field is allowed + + const char ref2[] = "enum E:byte { A } table T2 { f:E; } "; + test_conform(ref2, "enum E:int32 { A } table T2 { df:byte; f:E; }", + "field renamed to different type: T2.df (renamed from T2.f)"); } void UnionDeprecationTest(const std::string& tests_data_path) { @@ -138,4 +149,4 @@ void UnionDeprecationTest(const std::string& tests_data_path) { } } // namespace tests -} // namespace flatbuffers \ No newline at end of file +} // namespace flatbuffers