Commit 718ddea5 authored by David Reiss's avatar David Reiss Committed by Robert

[Go] Make enums into real types, add String() (#5235)

* [Go] Make enums into real types, add String()

This changes the generated code for enums: instead of type aliases,
they're now distinct types, allowing for better type-checking. Some
client code may have to be changed to add casts.

Enum types now have a String() method, so they implement fmt.Stringer.

An EnumValues map is now generated, in addition to the existing
EnumNames map, to easily map strings to values.

Generated enum files are now gofmt-clean.

Fixes #5207

* use example.ColorGreen explicitly

* use valid enum value in mutation test, add new test for "invalid" enum

* add length check and comment
parent 8d86b534
...@@ -83,7 +83,7 @@ class GoGenerator : public BaseGenerator { ...@@ -83,7 +83,7 @@ class GoGenerator : public BaseGenerator {
if (parser_.opts.one_file) { if (parser_.opts.one_file) {
one_file_code += enumcode; one_file_code += enumcode;
} else { } else {
if (!SaveType(**it, enumcode, false)) return false; if (!SaveType(**it, enumcode, false, true)) return false;
} }
} }
...@@ -95,13 +95,14 @@ class GoGenerator : public BaseGenerator { ...@@ -95,13 +95,14 @@ class GoGenerator : public BaseGenerator {
if (parser_.opts.one_file) { if (parser_.opts.one_file) {
one_file_code += declcode; one_file_code += declcode;
} else { } else {
if (!SaveType(**it, declcode, true)) return false; if (!SaveType(**it, declcode, true, false)) return false;
} }
} }
if (parser_.opts.one_file) { if (parser_.opts.one_file) {
std::string code = ""; std::string code = "";
BeginFile(LastNamespacePart(go_namespace_), true, &code); const bool is_enum = !parser_.enums_.vec.empty();
BeginFile(LastNamespacePart(go_namespace_), true, is_enum, &code);
code += one_file_code; code += one_file_code;
const std::string filename = GeneratedFileName(path_, file_name_); const std::string filename = GeneratedFileName(path_, file_name_);
return SaveFile(filename.c_str(), code, false); return SaveFile(filename.c_str(), code, false);
...@@ -140,7 +141,7 @@ class GoGenerator : public BaseGenerator { ...@@ -140,7 +141,7 @@ class GoGenerator : public BaseGenerator {
code += "\n}\n\n"; code += "\n}\n\n";
} }
// Construct the name of the type alias for this enum. // Construct the name of the type for this enum.
std::string GetEnumTypeName(const EnumDef &enum_def) { std::string GetEnumTypeName(const EnumDef &enum_def) {
return WrapInNameSpaceAndTrack(cur_name_space_, GoIdentity(enum_def.name)); return WrapInNameSpaceAndTrack(cur_name_space_, GoIdentity(enum_def.name));
} }
...@@ -148,8 +149,8 @@ class GoGenerator : public BaseGenerator { ...@@ -148,8 +149,8 @@ class GoGenerator : public BaseGenerator {
// Create a type for the enum values. // Create a type for the enum values.
void GenEnumType(const EnumDef &enum_def, std::string *code_ptr) { void GenEnumType(const EnumDef &enum_def, std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "type " + GetEnumTypeName(enum_def) + " = "; code += "type " + GetEnumTypeName(enum_def) + " ";
code += GenTypeBasic(enum_def.underlying_type) + "\n"; code += GenTypeBasic(enum_def.underlying_type) + "\n\n";
} }
// Begin enum code with a class declaration. // Begin enum code with a class declaration.
...@@ -160,12 +161,13 @@ class GoGenerator : public BaseGenerator { ...@@ -160,12 +161,13 @@ class GoGenerator : public BaseGenerator {
// A single enum member. // A single enum member.
void EnumMember(const EnumDef &enum_def, const EnumVal &ev, void EnumMember(const EnumDef &enum_def, const EnumVal &ev,
std::string *code_ptr) { const int max_name_length, std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "\t"; code += "\t";
code += enum_def.name; code += enum_def.name;
code += ev.name; code += ev.name;
code += " "; code += " ";
code += std::string(max_name_length - ev.name.length(), ' ');
code += GetEnumTypeName(enum_def); code += GetEnumTypeName(enum_def);
code += " = "; code += " = ";
code += enum_def.ToString(ev) + "\n"; code += enum_def.ToString(ev) + "\n";
...@@ -177,7 +179,7 @@ class GoGenerator : public BaseGenerator { ...@@ -177,7 +179,7 @@ class GoGenerator : public BaseGenerator {
code += ")\n\n"; code += ")\n\n";
} }
// Begin enum name code. // Begin enum name map.
void BeginEnumNames(const EnumDef &enum_def, std::string *code_ptr) { void BeginEnumNames(const EnumDef &enum_def, std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "var EnumNames"; code += "var EnumNames";
...@@ -187,22 +189,63 @@ class GoGenerator : public BaseGenerator { ...@@ -187,22 +189,63 @@ class GoGenerator : public BaseGenerator {
// A single enum name member. // A single enum name member.
void EnumNameMember(const EnumDef &enum_def, const EnumVal ev, void EnumNameMember(const EnumDef &enum_def, const EnumVal ev,
std::string *code_ptr) { const int max_name_length, std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "\t"; code += "\t";
code += enum_def.name; code += enum_def.name;
code += ev.name; code += ev.name;
code += ":\""; code += ": ";
code += std::string(max_name_length - ev.name.length(), ' ');
code += "\"";
code += ev.name; code += ev.name;
code += "\",\n"; code += "\",\n";
} }
// End enum name code. // End enum name map.
void EndEnumNames(std::string *code_ptr) { void EndEnumNames(std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code += "}\n\n"; code += "}\n\n";
} }
// Generate String() method on enum type.
void EnumStringer(const EnumDef &enum_def, std::string *code_ptr) {
std::string &code = *code_ptr;
code += "func (v " + enum_def.name + ") String() string {\n";
code += "\tif s, ok := EnumNames" + enum_def.name + "[v]; ok {\n";
code += "\t\treturn s\n";
code += "\t}\n";
code += "\treturn \""+ enum_def.name;
code += "(\" + strconv.FormatInt(int64(v), 10) + \")\"\n";
code += "}\n\n";
}
// Begin enum value map.
void BeginEnumValues(const EnumDef &enum_def, std::string *code_ptr) {
std::string &code = *code_ptr;
code += "var EnumValues";
code += enum_def.name;
code += " = map[string]" + GetEnumTypeName(enum_def) + "{\n";
}
// A single enum value member.
void EnumValueMember(const EnumDef &enum_def, const EnumVal ev,
const int max_name_length, std::string *code_ptr) {
std::string &code = *code_ptr;
code += "\t\"";
code += ev.name;
code += "\": ";
code += std::string(max_name_length - ev.name.length(), ' ');
code += enum_def.name;
code += ev.name;
code += ",\n";
}
// End enum value map.
void EndEnumValues(std::string *code_ptr) {
std::string &code = *code_ptr;
code += "}\n\n";
}
// Initialize a new struct or table from existing data. // Initialize a new struct or table from existing data.
void NewRootTypeFromBuffer(const StructDef &struct_def, void NewRootTypeFromBuffer(const StructDef &struct_def,
std::string *code_ptr) { std::string *code_ptr) {
...@@ -281,9 +324,11 @@ class GoGenerator : public BaseGenerator { ...@@ -281,9 +324,11 @@ class GoGenerator : public BaseGenerator {
GenReceiver(struct_def, code_ptr); GenReceiver(struct_def, code_ptr);
code += " " + MakeCamel(field.name); code += " " + MakeCamel(field.name);
code += "() " + TypeName(field) + " {\n"; code += "() " + TypeName(field) + " {\n";
code += "\treturn " + getter; code += "\treturn " + CastToEnum(
code += "(rcv._tab.Pos + flatbuffers.UOffsetT("; field.value.type,
code += NumToString(field.value.offset) + "))\n}\n"; getter + "(rcv._tab.Pos + flatbuffers.UOffsetT(" +
NumToString(field.value.offset) + "))");
code += "\n}\n";
} }
// Get the value of a table's scalar. // Get the value of a table's scalar.
...@@ -295,8 +340,9 @@ class GoGenerator : public BaseGenerator { ...@@ -295,8 +340,9 @@ class GoGenerator : public BaseGenerator {
GenReceiver(struct_def, code_ptr); GenReceiver(struct_def, code_ptr);
code += " " + MakeCamel(field.name); code += " " + MakeCamel(field.name);
code += "() " + TypeName(field) + " "; code += "() " + TypeName(field) + " ";
code += OffsetPrefix(field) + "\t\treturn " + getter; code += OffsetPrefix(field) + "\t\treturn ";
code += "(o + rcv._tab.Pos)\n\t}\n"; code += CastToEnum(field.value.type, getter + "(o + rcv._tab.Pos)");
code += "\n\t}\n";
code += "\treturn " + GenConstant(field) + "\n"; code += "\treturn " + GenConstant(field) + "\n";
code += "}\n\n"; code += "}\n\n";
} }
...@@ -364,7 +410,7 @@ class GoGenerator : public BaseGenerator { ...@@ -364,7 +410,7 @@ class GoGenerator : public BaseGenerator {
std::string &code = *code_ptr; std::string &code = *code_ptr;
GenReceiver(struct_def, code_ptr); GenReceiver(struct_def, code_ptr);
code += " " + MakeCamel(field.name) + "("; code += " " + MakeCamel(field.name) + "(";
code += "obj " + TypeName(field) + ") bool "; code += "obj " + GenTypePointer(field.value.type) + ") bool ";
code += OffsetPrefix(field); code += OffsetPrefix(field);
code += "\t\t" + GenGetter(field.value.type); code += "\t\t" + GenGetter(field.value.type);
code += "(obj, o)\n\t\treturn true\n\t}\n"; code += "(obj, o)\n\t\treturn true\n\t}\n";
...@@ -395,8 +441,7 @@ class GoGenerator : public BaseGenerator { ...@@ -395,8 +441,7 @@ class GoGenerator : public BaseGenerator {
code += "}\n\n"; code += "}\n\n";
} }
// Get the value of a vector's non-struct member. Uses a named return // Get the value of a vector's non-struct member.
// argument to conveniently set the zero value for the result.
void GetMemberOfVectorOfNonStruct(const StructDef &struct_def, void GetMemberOfVectorOfNonStruct(const StructDef &struct_def,
const FieldDef &field, const FieldDef &field,
std::string *code_ptr) { std::string *code_ptr) {
...@@ -408,10 +453,11 @@ class GoGenerator : public BaseGenerator { ...@@ -408,10 +453,11 @@ class GoGenerator : public BaseGenerator {
code += "(j int) " + TypeName(field) + " "; code += "(j int) " + TypeName(field) + " ";
code += OffsetPrefix(field); code += OffsetPrefix(field);
code += "\t\ta := rcv._tab.Vector(o)\n"; code += "\t\ta := rcv._tab.Vector(o)\n";
code += "\t\treturn " + GenGetter(field.value.type) + "("; code += "\t\treturn " + CastToEnum(
code += "a + flatbuffers.UOffsetT(j*"; field.value.type,
code += NumToString(InlineSize(vectortype)) + "))\n"; GenGetter(field.value.type) + "(a + flatbuffers.UOffsetT(j*" +
code += "\t}\n"; NumToString(InlineSize(vectortype)) + "))");
code += "\n\t}\n";
if (vectortype.base_type == BASE_TYPE_STRING) { if (vectortype.base_type == BASE_TYPE_STRING) {
code += "\treturn nil\n"; code += "\treturn nil\n";
} else if (vectortype.base_type == BASE_TYPE_BOOL) { } else if (vectortype.base_type == BASE_TYPE_BOOL) {
...@@ -609,7 +655,8 @@ class GoGenerator : public BaseGenerator { ...@@ -609,7 +655,8 @@ class GoGenerator : public BaseGenerator {
code += " Mutate" + MakeCamel(field.name); code += " Mutate" + MakeCamel(field.name);
code += "(n " + TypeName(field) + ") bool {\n\treturn " + setter; code += "(n " + TypeName(field) + ") bool {\n\treturn " + setter;
code += "(rcv._tab.Pos+flatbuffers.UOffsetT("; code += "(rcv._tab.Pos+flatbuffers.UOffsetT(";
code += NumToString(field.value.offset) + "), n)\n}\n\n"; code += NumToString(field.value.offset) + "), ";
code += CastToBaseType(field.value.type, "n") + ")\n}\n\n";
} }
// Mutate the value of a table's scalar. // Mutate the value of a table's scalar.
...@@ -622,7 +669,8 @@ class GoGenerator : public BaseGenerator { ...@@ -622,7 +669,8 @@ class GoGenerator : public BaseGenerator {
GenReceiver(struct_def, code_ptr); GenReceiver(struct_def, code_ptr);
code += " Mutate" + MakeCamel(field.name); code += " Mutate" + MakeCamel(field.name);
code += "(n " + TypeName(field) + ") bool {\n\treturn "; code += "(n " + TypeName(field) + ") bool {\n\treturn ";
code += setter + "(" + NumToString(field.value.offset) + ", n)\n"; code += setter + "(" + NumToString(field.value.offset) + ", ";
code += CastToBaseType(field.value.type, "n") + ")\n";
code += "}\n\n"; code += "}\n\n";
} }
...@@ -641,7 +689,8 @@ class GoGenerator : public BaseGenerator { ...@@ -641,7 +689,8 @@ class GoGenerator : public BaseGenerator {
code += "\t\ta := rcv._tab.Vector(o)\n"; code += "\t\ta := rcv._tab.Vector(o)\n";
code += "\t\treturn " + setter + "("; code += "\t\treturn " + setter + "(";
code += "a+flatbuffers.UOffsetT(j*"; code += "a+flatbuffers.UOffsetT(j*";
code += NumToString(InlineSize(vectortype)) + "), n)\n"; code += NumToString(InlineSize(vectortype)) + "), ";
code += CastToBaseType(vectortype, "n") + ")\n";
code += "\t}\n"; code += "\t}\n";
code += "\treturn false\n"; code += "\treturn false\n";
code += "}\n\n"; code += "}\n\n";
...@@ -726,6 +775,7 @@ class GoGenerator : public BaseGenerator { ...@@ -726,6 +775,7 @@ class GoGenerator : public BaseGenerator {
void GenEnum(const EnumDef &enum_def, std::string *code_ptr) { void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
if (enum_def.generated) return; if (enum_def.generated) return;
const int max_name_length = MaxNameLength(enum_def);
cur_name_space_ = enum_def.defined_namespace; cur_name_space_ = enum_def.defined_namespace;
GenComment(enum_def.doc_comment, code_ptr, nullptr); GenComment(enum_def.doc_comment, code_ptr, nullptr);
...@@ -734,16 +784,26 @@ class GoGenerator : public BaseGenerator { ...@@ -734,16 +784,26 @@ class GoGenerator : public BaseGenerator {
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
auto &ev = **it; auto &ev = **it;
GenComment(ev.doc_comment, code_ptr, nullptr, "\t"); GenComment(ev.doc_comment, code_ptr, nullptr, "\t");
EnumMember(enum_def, ev, code_ptr); EnumMember(enum_def, ev, max_name_length, code_ptr);
} }
EndEnum(code_ptr); EndEnum(code_ptr);
BeginEnumNames(enum_def, code_ptr); BeginEnumNames(enum_def, code_ptr);
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
auto &ev = **it; auto &ev = **it;
EnumNameMember(enum_def, ev, code_ptr); EnumNameMember(enum_def, ev, max_name_length, code_ptr);
} }
EndEnumNames(code_ptr); EndEnumNames(code_ptr);
BeginEnumValues(enum_def, code_ptr);
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
++it) {
auto &ev = **it;
EnumValueMember(enum_def, ev, max_name_length, code_ptr);
}
EndEnumValues(code_ptr);
EnumStringer(enum_def, code_ptr);
} }
// Returns the function name that is able to read a value of the given type. // Returns the function name that is able to read a value of the given type.
...@@ -788,7 +848,7 @@ class GoGenerator : public BaseGenerator { ...@@ -788,7 +848,7 @@ class GoGenerator : public BaseGenerator {
} }
std::string GenTypeGet(const Type &type) { std::string GenTypeGet(const Type &type) {
if (type.enum_def != nullptr && !type.enum_def->is_union) { if (type.enum_def != nullptr) {
return GetEnumTypeName(*type.enum_def); return GetEnumTypeName(*type.enum_def);
} }
return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type); return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type);
...@@ -798,6 +858,26 @@ class GoGenerator : public BaseGenerator { ...@@ -798,6 +858,26 @@ class GoGenerator : public BaseGenerator {
return GenTypeGet(field.value.type); return GenTypeGet(field.value.type);
} }
// If type is an enum, returns value with a cast to the enum type, otherwise
// returns value as-is.
std::string CastToEnum(const Type &type, std::string value) {
if (type.enum_def == nullptr) {
return value;
} else {
return GenTypeGet(type) + "(" + value + ")";
}
}
// If type is an enum, returns value with a cast to the enum base type,
// otherwise returns value as-is.
std::string CastToBaseType(const Type &type, std::string value) {
if (type.enum_def == nullptr) {
return value;
} else {
return GenTypeBasic(type) + "(" + value + ")";
}
}
std::string GenConstant(const FieldDef &field) { std::string GenConstant(const FieldDef &field) {
switch (field.value.type.base_type) { switch (field.value.type.base_type) {
case BASE_TYPE_BOOL: return field.value.constant == "0" ? "false" : "true";; case BASE_TYPE_BOOL: return field.value.constant == "0" ? "false" : "true";;
...@@ -816,12 +896,15 @@ class GoGenerator : public BaseGenerator { ...@@ -816,12 +896,15 @@ class GoGenerator : public BaseGenerator {
} }
// Begin by declaring namespace and imports. // Begin by declaring namespace and imports.
void BeginFile(const std::string name_space_name, const bool needs_imports, void BeginFile(const std::string name_space_name, const bool needs_imports,
std::string *code_ptr) { const bool is_enum, std::string *code_ptr) {
std::string &code = *code_ptr; std::string &code = *code_ptr;
code = code + "// Code generated by the FlatBuffers compiler. DO NOT EDIT.\n\n"; code = code + "// Code generated by the FlatBuffers compiler. DO NOT EDIT.\n\n";
code += "package " + name_space_name + "\n\n"; code += "package " + name_space_name + "\n\n";
if (needs_imports) { if (needs_imports) {
code += "import (\n"; code += "import (\n";
if (is_enum) {
code += "\t\"strconv\"\n\n";
}
if (!parser_.opts.go_import.empty()) { if (!parser_.opts.go_import.empty()) {
code += "\tflatbuffers \"" + parser_.opts.go_import + "\"\n"; code += "\tflatbuffers \"" + parser_.opts.go_import + "\"\n";
} else { } else {
...@@ -837,19 +920,27 @@ class GoGenerator : public BaseGenerator { ...@@ -837,19 +920,27 @@ class GoGenerator : public BaseGenerator {
} }
} }
code += ")\n\n"; code += ")\n\n";
} else {
if (is_enum) {
code += "import \"strconv\"\n\n";
}
} }
} }
// Save out the generated code for a Go Table type. // Save out the generated code for a Go Table type.
bool SaveType(const Definition &def, const std::string &classcode, bool SaveType(const Definition &def, const std::string &classcode,
bool needs_imports) { const bool needs_imports, const bool is_enum) {
if (!classcode.length()) return true; if (!classcode.length()) return true;
Namespace &ns = go_namespace_.components.empty() ? *def.defined_namespace Namespace &ns = go_namespace_.components.empty() ? *def.defined_namespace
: go_namespace_; : go_namespace_;
std::string code = ""; std::string code = "";
BeginFile(LastNamespacePart(ns), needs_imports, &code); BeginFile(LastNamespacePart(ns), needs_imports, is_enum, &code);
code += classcode; code += classcode;
// Strip extra newlines at end of file to make it gofmt-clean.
while (code.length() > 2 && code.substr(code.length() - 2) == "\n\n") {
code.pop_back();
}
std::string filename = NamespaceDir(ns) + def.name + ".go"; std::string filename = NamespaceDir(ns) + def.name + ".go";
return SaveFile(filename.c_str(), code, false); return SaveFile(filename.c_str(), code, false);
} }
...@@ -897,6 +988,16 @@ class GoGenerator : public BaseGenerator { ...@@ -897,6 +988,16 @@ class GoGenerator : public BaseGenerator {
} }
const Namespace *CurrentNameSpace() const { return cur_name_space_; } const Namespace *CurrentNameSpace() const { return cur_name_space_; }
static int MaxNameLength(const EnumDef &enum_def) {
int max = 0;
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
++it) {
const int length = (*it)->name.length();
max = length > max ? length : max;
}
return max;
}
}; };
} // namespace go } // namespace go
......
...@@ -2,7 +2,10 @@ ...@@ -2,7 +2,10 @@
package Example package Example
type Any = byte import "strconv"
type Any byte
const ( const (
AnyNONE Any = 0 AnyNONE Any = 0
AnyMonster Any = 1 AnyMonster Any = 1
...@@ -11,9 +14,22 @@ const ( ...@@ -11,9 +14,22 @@ const (
) )
var EnumNamesAny = map[Any]string{ var EnumNamesAny = map[Any]string{
AnyNONE:"NONE", AnyNONE: "NONE",
AnyMonster:"Monster", AnyMonster: "Monster",
AnyTestSimpleTableWithEnum:"TestSimpleTableWithEnum", AnyTestSimpleTableWithEnum: "TestSimpleTableWithEnum",
AnyMyGame_Example2_Monster:"MyGame_Example2_Monster", AnyMyGame_Example2_Monster: "MyGame_Example2_Monster",
} }
var EnumValuesAny = map[string]Any{
"NONE": AnyNONE,
"Monster": AnyMonster,
"TestSimpleTableWithEnum": AnyTestSimpleTableWithEnum,
"MyGame_Example2_Monster": AnyMyGame_Example2_Monster,
}
func (v Any) String() string {
if s, ok := EnumNamesAny[v]; ok {
return s
}
return "Any(" + strconv.FormatInt(int64(v), 10) + ")"
}
...@@ -2,7 +2,10 @@ ...@@ -2,7 +2,10 @@
package Example package Example
type AnyAmbiguousAliases = byte import "strconv"
type AnyAmbiguousAliases byte
const ( const (
AnyAmbiguousAliasesNONE AnyAmbiguousAliases = 0 AnyAmbiguousAliasesNONE AnyAmbiguousAliases = 0
AnyAmbiguousAliasesM1 AnyAmbiguousAliases = 1 AnyAmbiguousAliasesM1 AnyAmbiguousAliases = 1
...@@ -11,9 +14,22 @@ const ( ...@@ -11,9 +14,22 @@ const (
) )
var EnumNamesAnyAmbiguousAliases = map[AnyAmbiguousAliases]string{ var EnumNamesAnyAmbiguousAliases = map[AnyAmbiguousAliases]string{
AnyAmbiguousAliasesNONE:"NONE", AnyAmbiguousAliasesNONE: "NONE",
AnyAmbiguousAliasesM1:"M1", AnyAmbiguousAliasesM1: "M1",
AnyAmbiguousAliasesM2:"M2", AnyAmbiguousAliasesM2: "M2",
AnyAmbiguousAliasesM3:"M3", AnyAmbiguousAliasesM3: "M3",
} }
var EnumValuesAnyAmbiguousAliases = map[string]AnyAmbiguousAliases{
"NONE": AnyAmbiguousAliasesNONE,
"M1": AnyAmbiguousAliasesM1,
"M2": AnyAmbiguousAliasesM2,
"M3": AnyAmbiguousAliasesM3,
}
func (v AnyAmbiguousAliases) String() string {
if s, ok := EnumNamesAnyAmbiguousAliases[v]; ok {
return s
}
return "AnyAmbiguousAliases(" + strconv.FormatInt(int64(v), 10) + ")"
}
...@@ -2,7 +2,10 @@ ...@@ -2,7 +2,10 @@
package Example package Example
type AnyUniqueAliases = byte import "strconv"
type AnyUniqueAliases byte
const ( const (
AnyUniqueAliasesNONE AnyUniqueAliases = 0 AnyUniqueAliasesNONE AnyUniqueAliases = 0
AnyUniqueAliasesM AnyUniqueAliases = 1 AnyUniqueAliasesM AnyUniqueAliases = 1
...@@ -11,9 +14,22 @@ const ( ...@@ -11,9 +14,22 @@ const (
) )
var EnumNamesAnyUniqueAliases = map[AnyUniqueAliases]string{ var EnumNamesAnyUniqueAliases = map[AnyUniqueAliases]string{
AnyUniqueAliasesNONE:"NONE", AnyUniqueAliasesNONE: "NONE",
AnyUniqueAliasesM:"M", AnyUniqueAliasesM: "M",
AnyUniqueAliasesT:"T", AnyUniqueAliasesT: "T",
AnyUniqueAliasesM2:"M2", AnyUniqueAliasesM2: "M2",
} }
var EnumValuesAnyUniqueAliases = map[string]AnyUniqueAliases{
"NONE": AnyUniqueAliasesNONE,
"M": AnyUniqueAliasesM,
"T": AnyUniqueAliasesT,
"M2": AnyUniqueAliasesM2,
}
func (v AnyUniqueAliases) String() string {
if s, ok := EnumNamesAnyUniqueAliases[v]; ok {
return s
}
return "AnyUniqueAliases(" + strconv.FormatInt(int64(v), 10) + ")"
}
...@@ -2,7 +2,10 @@ ...@@ -2,7 +2,10 @@
package Example package Example
type Color = byte import "strconv"
type Color byte
const ( const (
ColorRed Color = 1 ColorRed Color = 1
ColorGreen Color = 2 ColorGreen Color = 2
...@@ -10,8 +13,20 @@ const ( ...@@ -10,8 +13,20 @@ const (
) )
var EnumNamesColor = map[Color]string{ var EnumNamesColor = map[Color]string{
ColorRed:"Red", ColorRed: "Red",
ColorGreen:"Green", ColorGreen: "Green",
ColorBlue:"Blue", ColorBlue: "Blue",
} }
var EnumValuesColor = map[string]Color{
"Red": ColorRed,
"Green": ColorGreen,
"Blue": ColorBlue,
}
func (v Color) String() string {
if s, ok := EnumNamesColor[v]; ok {
return s
}
return "Color(" + strconv.FormatInt(int64(v), 10) + ")"
}
...@@ -111,25 +111,25 @@ func (rcv *Monster) MutateInventory(j int, n byte) bool { ...@@ -111,25 +111,25 @@ func (rcv *Monster) MutateInventory(j int, n byte) bool {
func (rcv *Monster) Color() Color { func (rcv *Monster) Color() Color {
o := flatbuffers.UOffsetT(rcv._tab.Offset(16)) o := flatbuffers.UOffsetT(rcv._tab.Offset(16))
if o != 0 { if o != 0 {
return rcv._tab.GetByte(o + rcv._tab.Pos) return Color(rcv._tab.GetByte(o + rcv._tab.Pos))
} }
return 8 return 8
} }
func (rcv *Monster) MutateColor(n Color) bool { func (rcv *Monster) MutateColor(n Color) bool {
return rcv._tab.MutateByteSlot(16, n) return rcv._tab.MutateByteSlot(16, byte(n))
} }
func (rcv *Monster) TestType() byte { func (rcv *Monster) TestType() Any {
o := flatbuffers.UOffsetT(rcv._tab.Offset(18)) o := flatbuffers.UOffsetT(rcv._tab.Offset(18))
if o != 0 { if o != 0 {
return rcv._tab.GetByte(o + rcv._tab.Pos) return Any(rcv._tab.GetByte(o + rcv._tab.Pos))
} }
return 0 return 0
} }
func (rcv *Monster) MutateTestType(n byte) bool { func (rcv *Monster) MutateTestType(n Any) bool {
return rcv._tab.MutateByteSlot(18, n) return rcv._tab.MutateByteSlot(18, byte(n))
} }
func (rcv *Monster) Test(obj *flatbuffers.Table) bool { func (rcv *Monster) Test(obj *flatbuffers.Table) bool {
...@@ -739,16 +739,16 @@ func (rcv *Monster) MutateVectorOfNonOwningReferences(j int, n uint64) bool { ...@@ -739,16 +739,16 @@ func (rcv *Monster) MutateVectorOfNonOwningReferences(j int, n uint64) bool {
return false return false
} }
func (rcv *Monster) AnyUniqueType() byte { func (rcv *Monster) AnyUniqueType() AnyUniqueAliases {
o := flatbuffers.UOffsetT(rcv._tab.Offset(90)) o := flatbuffers.UOffsetT(rcv._tab.Offset(90))
if o != 0 { if o != 0 {
return rcv._tab.GetByte(o + rcv._tab.Pos) return AnyUniqueAliases(rcv._tab.GetByte(o + rcv._tab.Pos))
} }
return 0 return 0
} }
func (rcv *Monster) MutateAnyUniqueType(n byte) bool { func (rcv *Monster) MutateAnyUniqueType(n AnyUniqueAliases) bool {
return rcv._tab.MutateByteSlot(90, n) return rcv._tab.MutateByteSlot(90, byte(n))
} }
func (rcv *Monster) AnyUnique(obj *flatbuffers.Table) bool { func (rcv *Monster) AnyUnique(obj *flatbuffers.Table) bool {
...@@ -760,16 +760,16 @@ func (rcv *Monster) AnyUnique(obj *flatbuffers.Table) bool { ...@@ -760,16 +760,16 @@ func (rcv *Monster) AnyUnique(obj *flatbuffers.Table) bool {
return false return false
} }
func (rcv *Monster) AnyAmbiguousType() byte { func (rcv *Monster) AnyAmbiguousType() AnyAmbiguousAliases {
o := flatbuffers.UOffsetT(rcv._tab.Offset(94)) o := flatbuffers.UOffsetT(rcv._tab.Offset(94))
if o != 0 { if o != 0 {
return rcv._tab.GetByte(o + rcv._tab.Pos) return AnyAmbiguousAliases(rcv._tab.GetByte(o + rcv._tab.Pos))
} }
return 0 return 0
} }
func (rcv *Monster) MutateAnyAmbiguousType(n byte) bool { func (rcv *Monster) MutateAnyAmbiguousType(n AnyAmbiguousAliases) bool {
return rcv._tab.MutateByteSlot(94, n) return rcv._tab.MutateByteSlot(94, byte(n))
} }
func (rcv *Monster) AnyAmbiguous(obj *flatbuffers.Table) bool { func (rcv *Monster) AnyAmbiguous(obj *flatbuffers.Table) bool {
...@@ -785,7 +785,7 @@ func (rcv *Monster) VectorOfEnums(j int) Color { ...@@ -785,7 +785,7 @@ func (rcv *Monster) VectorOfEnums(j int) Color {
o := flatbuffers.UOffsetT(rcv._tab.Offset(98)) o := flatbuffers.UOffsetT(rcv._tab.Offset(98))
if o != 0 { if o != 0 {
a := rcv._tab.Vector(o) a := rcv._tab.Vector(o)
return rcv._tab.GetByte(a + flatbuffers.UOffsetT(j*1)) return Color(rcv._tab.GetByte(a + flatbuffers.UOffsetT(j*1)))
} }
return 0 return 0
} }
...@@ -810,7 +810,7 @@ func (rcv *Monster) MutateVectorOfEnums(j int, n Color) bool { ...@@ -810,7 +810,7 @@ func (rcv *Monster) MutateVectorOfEnums(j int, n Color) bool {
o := flatbuffers.UOffsetT(rcv._tab.Offset(98)) o := flatbuffers.UOffsetT(rcv._tab.Offset(98))
if o != 0 { if o != 0 {
a := rcv._tab.Vector(o) a := rcv._tab.Vector(o)
return rcv._tab.MutateByte(a+flatbuffers.UOffsetT(j*1), n) return rcv._tab.MutateByte(a+flatbuffers.UOffsetT(j*1), byte(n))
} }
return false return false
} }
......
...@@ -29,13 +29,13 @@ func (rcv *TestSimpleTableWithEnum) Table() flatbuffers.Table { ...@@ -29,13 +29,13 @@ func (rcv *TestSimpleTableWithEnum) Table() flatbuffers.Table {
func (rcv *TestSimpleTableWithEnum) Color() Color { func (rcv *TestSimpleTableWithEnum) Color() Color {
o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
if o != 0 { if o != 0 {
return rcv._tab.GetByte(o + rcv._tab.Pos) return Color(rcv._tab.GetByte(o + rcv._tab.Pos))
} }
return 2 return 2
} }
func (rcv *TestSimpleTableWithEnum) MutateColor(n Color) bool { func (rcv *TestSimpleTableWithEnum) MutateColor(n Color) bool {
return rcv._tab.MutateByteSlot(4, n) return rcv._tab.MutateByteSlot(4, byte(n))
} }
func TestSimpleTableWithEnumStart(builder *flatbuffers.Builder) { func TestSimpleTableWithEnumStart(builder *flatbuffers.Builder) {
......
...@@ -48,10 +48,10 @@ func (rcv *Vec3) MutateTest1(n float64) bool { ...@@ -48,10 +48,10 @@ func (rcv *Vec3) MutateTest1(n float64) bool {
} }
func (rcv *Vec3) Test2() Color { func (rcv *Vec3) Test2() Color {
return rcv._tab.GetByte(rcv._tab.Pos + flatbuffers.UOffsetT(24)) return Color(rcv._tab.GetByte(rcv._tab.Pos + flatbuffers.UOffsetT(24)))
} }
func (rcv *Vec3) MutateTest2(n Color) bool { func (rcv *Vec3) MutateTest2(n Color) bool {
return rcv._tab.MutateByte(rcv._tab.Pos+flatbuffers.UOffsetT(24), n) return rcv._tab.MutateByte(rcv._tab.Pos+flatbuffers.UOffsetT(24), byte(n))
} }
func (rcv *Vec3) Test3(obj *Test) *Test { func (rcv *Vec3) Test3(obj *Test) *Test {
......
...@@ -106,6 +106,12 @@ func TestAll(t *testing.T) { ...@@ -106,6 +106,12 @@ func TestAll(t *testing.T) {
// Verify the enum names // Verify the enum names
CheckEnumNames(t.Fatalf) CheckEnumNames(t.Fatalf)
// Verify enum String methods
CheckEnumString(t.Fatalf)
// Verify the enum values maps
CheckEnumValues(t.Fatalf)
// Verify that the Go code used in FlatBuffers documentation passes // Verify that the Go code used in FlatBuffers documentation passes
// some sanity checks: // some sanity checks:
CheckDocExample(generated, off, t.Fatalf) CheckDocExample(generated, off, t.Fatalf)
...@@ -199,8 +205,8 @@ func CheckReadBuffer(buf []byte, offset flatbuffers.UOffsetT, fail func(string, ...@@ -199,8 +205,8 @@ func CheckReadBuffer(buf []byte, offset flatbuffers.UOffsetT, fail func(string,
fail(FailString("Pos.Test1", float64(3.0), got)) fail(FailString("Pos.Test1", float64(3.0), got))
} }
if got := vec.Test2(); uint8(2) != got { if got := vec.Test2(); example.ColorGreen != got {
fail(FailString("Pos.Test2", uint8(2), got)) fail(FailString("Pos.Test2", example.ColorGreen, got))
} }
// initialize a Test from Test3(...) // initialize a Test from Test3(...)
...@@ -331,7 +337,7 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string ...@@ -331,7 +337,7 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string
testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(2.0) }}, testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(2.0) }},
testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(3.0) }}, testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(3.0) }},
testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(3.0) }}, testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(3.0) }},
testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == uint8(2) }}, testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == example.ColorGreen }},
testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(5) }}, testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(5) }},
testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(6) }}, testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(6) }},
testcase{"Inventory[2]", func() bool { return monster.Inventory(2) == byte(2) }}, testcase{"Inventory[2]", func() bool { return monster.Inventory(2) == byte(2) }},
...@@ -345,7 +351,7 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string ...@@ -345,7 +351,7 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string
testcase{"Pos.Y", func() bool { return monster.Pos(nil).MutateY(20.0) }}, testcase{"Pos.Y", func() bool { return monster.Pos(nil).MutateY(20.0) }},
testcase{"Pos.Z", func() bool { return monster.Pos(nil).MutateZ(30.0) }}, testcase{"Pos.Z", func() bool { return monster.Pos(nil).MutateZ(30.0) }},
testcase{"Pos.Test1", func() bool { return monster.Pos(nil).MutateTest1(30.0) }}, testcase{"Pos.Test1", func() bool { return monster.Pos(nil).MutateTest1(30.0) }},
testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(20) }}, testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(example.ColorBlue) }},
testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).MutateA(50) }}, testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).MutateA(50) }},
testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).MutateB(60) }}, testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).MutateB(60) }},
testcase{"Inventory[2]", func() bool { return monster.MutateInventory(2, 200) }}, testcase{"Inventory[2]", func() bool { return monster.MutateInventory(2, 200) }},
...@@ -359,12 +365,17 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string ...@@ -359,12 +365,17 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string
testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(20.0) }}, testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(20.0) }},
testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(30.0) }}, testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(30.0) }},
testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(30.0) }}, testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(30.0) }},
testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == uint8(20) }}, testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == example.ColorBlue }},
testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(50) }}, testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(50) }},
testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(60) }}, testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(60) }},
testcase{"Inventory[2]", func() bool { return monster.Inventory(2) == byte(200) }}, testcase{"Inventory[2]", func() bool { return monster.Inventory(2) == byte(200) }},
} }
testInvalidEnumValues := []testcase{
testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(example.Color(20)) }},
testcase{"Pos.Test2", func() bool { return monster.Pos(nil).Test2() == example.Color(20) }},
}
// make sure original values are okay // make sure original values are okay
for _, t := range testForOriginalValues { for _, t := range testForOriginalValues {
if !t.testfn() { if !t.testfn() {
...@@ -400,6 +411,14 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string ...@@ -400,6 +411,14 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string
} }
} }
// a couple extra tests for "invalid" enum values, which don't correspond to
// anything in the schema, but are allowed
for _, t := range testInvalidEnumValues {
if !t.testfn() {
fail("field '" + t.field + "' doesn't work with an invalid enum value")
}
}
// reverting all fields to original values should // reverting all fields to original values should
// re-create the original buffer. Mutate all fields // re-create the original buffer. Mutate all fields
// back to their original values and compare buffers. // back to their original values and compare buffers.
...@@ -412,7 +431,7 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string ...@@ -412,7 +431,7 @@ func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string
monster.Pos(nil).MutateY(2.0) monster.Pos(nil).MutateY(2.0)
monster.Pos(nil).MutateZ(3.0) monster.Pos(nil).MutateZ(3.0)
monster.Pos(nil).MutateTest1(3.0) monster.Pos(nil).MutateTest1(3.0)
monster.Pos(nil).MutateTest2(2) monster.Pos(nil).MutateTest2(example.ColorGreen)
monster.Pos(nil).Test3(nil).MutateA(5) monster.Pos(nil).Test3(nil).MutateA(5)
monster.Pos(nil).Test3(nil).MutateB(6) monster.Pos(nil).Test3(nil).MutateB(6)
monster.MutateInventory(2, 2) monster.MutateInventory(2, 2)
...@@ -1379,7 +1398,6 @@ func CheckFinishedBytesError(fail func(string, ...interface{})) { ...@@ -1379,7 +1398,6 @@ func CheckFinishedBytesError(fail func(string, ...interface{})) {
// CheckEnumNames checks that the generated enum names are correct. // CheckEnumNames checks that the generated enum names are correct.
func CheckEnumNames(fail func(string, ...interface{})) { func CheckEnumNames(fail func(string, ...interface{})) {
{ {
want := map[example.Any]string{ want := map[example.Any]string{
example.AnyNONE: "NONE", example.AnyNONE: "NONE",
example.AnyMonster: "Monster", example.AnyMonster: "Monster",
...@@ -1404,6 +1422,43 @@ func CheckEnumNames(fail func(string, ...interface{})) { ...@@ -1404,6 +1422,43 @@ func CheckEnumNames(fail func(string, ...interface{})) {
} }
} }
// CheckEnumString checks the String method on generated enum types.
func CheckEnumString(fail func(string, ...interface{})) {
if got := example.AnyMonster.String(); got != "Monster" {
fail("Monster.String: %q != %q", got, "Monster")
}
if got := fmt.Sprintf("color: %s", example.ColorGreen); got != "color: Green" {
fail("color.String: %q != %q", got, "color: Green")
}
}
// CheckEnumValues checks that the generated enum values maps are correct.
func CheckEnumValues(fail func(string, ...interface{})) {
{
want := map[string]example.Any{
"NONE": example.AnyNONE,
"Monster": example.AnyMonster,
"TestSimpleTableWithEnum": example.AnyTestSimpleTableWithEnum,
"MyGame_Example2_Monster": example.AnyMyGame_Example2_Monster,
}
got := example.EnumValuesAny
if !reflect.DeepEqual(got, want) {
fail("enum name is not equal")
}
}
{
want := map[string]example.Color{
"Red": example.ColorRed,
"Green": example.ColorGreen,
"Blue": example.ColorBlue,
}
got := example.EnumValuesColor
if !reflect.DeepEqual(got, want) {
fail("enum name is not equal")
}
}
}
// CheckDocExample checks that the code given in FlatBuffers documentation // CheckDocExample checks that the code given in FlatBuffers documentation
// is syntactically correct. // is syntactically correct.
func CheckDocExample(buf []byte, off flatbuffers.UOffsetT, fail func(string, ...interface{})) { func CheckDocExample(buf []byte, off flatbuffers.UOffsetT, fail func(string, ...interface{})) {
......
...@@ -2,7 +2,10 @@ ...@@ -2,7 +2,10 @@
package NamespaceB package NamespaceB
type EnumInNestedNS = int8 import "strconv"
type EnumInNestedNS int8
const ( const (
EnumInNestedNSA EnumInNestedNS = 0 EnumInNestedNSA EnumInNestedNS = 0
EnumInNestedNSB EnumInNestedNS = 1 EnumInNestedNSB EnumInNestedNS = 1
...@@ -10,8 +13,20 @@ const ( ...@@ -10,8 +13,20 @@ const (
) )
var EnumNamesEnumInNestedNS = map[EnumInNestedNS]string{ var EnumNamesEnumInNestedNS = map[EnumInNestedNS]string{
EnumInNestedNSA:"A", EnumInNestedNSA: "A",
EnumInNestedNSB:"B", EnumInNestedNSB: "B",
EnumInNestedNSC:"C", EnumInNestedNSC: "C",
} }
var EnumValuesEnumInNestedNS = map[string]EnumInNestedNS{
"A": EnumInNestedNSA,
"B": EnumInNestedNSB,
"C": EnumInNestedNSC,
}
func (v EnumInNestedNS) String() string {
if s, ok := EnumNamesEnumInNestedNS[v]; ok {
return s
}
return "EnumInNestedNS(" + strconv.FormatInt(int64(v), 10) + ")"
}
...@@ -44,13 +44,13 @@ func (rcv *TableInFirstNS) FooTable(obj *NamespaceA__NamespaceB.TableInNestedNS) ...@@ -44,13 +44,13 @@ func (rcv *TableInFirstNS) FooTable(obj *NamespaceA__NamespaceB.TableInNestedNS)
func (rcv *TableInFirstNS) FooEnum() EnumInNestedNS { func (rcv *TableInFirstNS) FooEnum() EnumInNestedNS {
o := flatbuffers.UOffsetT(rcv._tab.Offset(6)) o := flatbuffers.UOffsetT(rcv._tab.Offset(6))
if o != 0 { if o != 0 {
return rcv._tab.GetInt8(o + rcv._tab.Pos) return EnumInNestedNS(rcv._tab.GetInt8(o + rcv._tab.Pos))
} }
return 0 return 0
} }
func (rcv *TableInFirstNS) MutateFooEnum(n EnumInNestedNS) bool { func (rcv *TableInFirstNS) MutateFooEnum(n EnumInNestedNS) bool {
return rcv._tab.MutateInt8Slot(6, n) return rcv._tab.MutateInt8Slot(6, int8(n))
} }
func (rcv *TableInFirstNS) FooStruct(obj *NamespaceA__NamespaceB.StructInNestedNS) *NamespaceA__NamespaceB.StructInNestedNS { func (rcv *TableInFirstNS) FooStruct(obj *NamespaceA__NamespaceB.StructInNestedNS) *NamespaceA__NamespaceB.StructInNestedNS {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment