Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

protoparse: c++ scoping rules for enum values #401

Merged
merged 1 commit into from Jun 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 14 additions & 2 deletions desc/protoparse/linker.go
Expand Up @@ -213,7 +213,10 @@ func addEnumToPool(r *parseResult, pool map[string]proto.Message, errs *errorHan
return err
}
for _, evd := range ed.Value {
vfqn := fqn + "." + evd.GetName()
// protobuf name-scoping rules for enum values follow C++ scoping rules:
// the enum value name is a symbol in the *parent* scope (the one
// enclosing the enum).
vfqn := prefix + evd.GetName()
if err := addToPool(r, pool, errs, vfqn, evd); err != nil {
return err
}
Expand All @@ -238,7 +241,16 @@ func addServiceToPool(r *parseResult, pool map[string]proto.Message, errs *error
func addToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, fqn string, dsc proto.Message) error {
if d, ok := pool[fqn]; ok {
node := r.nodes[dsc]
if err := errs.handleErrorWithPos(node.Start(), "duplicate symbol %s: already defined as %s", fqn, descriptorType(d)); err != nil {
_, additionIsEnumVal := dsc.(*dpb.EnumValueDescriptorProto)
_, existingIsEnumVal := d.(*dpb.EnumValueDescriptorProto)
// because of weird scoping for enum values, provide more context in error message
// if this conflict is with an enum value
var suffix string
if additionIsEnumVal || existingIsEnumVal {
suffix = "; protobuf uses C++ scoping rules for enum values, so they exist in the scope enclosing the enum"
}
// TODO: also include the source location for the conflicting symbol
if err := errs.handleErrorWithPos(node.Start(), "duplicate symbol %s: already defined as %s%s", fqn, descriptorType(d), suffix); err != nil {
return err
}
}
Expand Down
6 changes: 6 additions & 0 deletions desc/protoparse/linker_test.go
Expand Up @@ -111,6 +111,12 @@ func TestLinkerValidation(t *testing.T) {
},
`foo.proto:1:8: cycle found in imports: "foo.proto" -> "foo2.proto" -> "foo.proto"`,
},
{
map[string]string{
"foo.proto": "enum foo { bar = 1; baz = 2; } enum fu { bar = 1; baz = 2; }",
},
`foo.proto:1:42: duplicate symbol bar: already defined as enum value; protobuf uses C++ scoping rules for enum values, so they exist in the scope enclosing the enum`,
},
{
map[string]string{
"foo.proto": "message foo {} enum foo { V = 0; }",
Expand Down
2 changes: 1 addition & 1 deletion desc/protoparse/reporting_test.go
Expand Up @@ -104,7 +104,7 @@ func TestErrorReporting(t *testing.T) {
`,
},
expectedErrs: []string{
"test.proto:8:49: duplicate symbol Bar.BAZ: already defined as enum value",
"test.proto:8:49: duplicate symbol BAZ: already defined as enum value; protobuf uses C++ scoping rules for enum values, so they exist in the scope enclosing the enum",
"test.proto:10:41: duplicate symbol Bar: already defined as enum",
"test.proto:12:49: duplicate symbol Bar.Foo: already defined as method",
"test.proto:12:58: method Bar.Foo: unknown request type Frob",
Expand Down