github.com/Big-big-orange/protoreflect@v0.0.0-20240408141420-285cedfdf6a4/desc/protoparse/linker_test.go (about)

     1  package protoparse
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"reflect"
     9  	"strings"
    10  	"testing"
    11  
    12  	protov1 "github.com/golang/protobuf/proto"
    13  	"google.golang.org/protobuf/encoding/protojson"
    14  	"google.golang.org/protobuf/proto"
    15  	"google.golang.org/protobuf/types/descriptorpb"
    16  
    17  	"github.com/Big-big-orange/protoreflect/desc"
    18  	_ "github.com/Big-big-orange/protoreflect/internal/testprotos"
    19  	"github.com/Big-big-orange/protoreflect/internal/testutil"
    20  )
    21  
    22  func TestSimpleLink(t *testing.T) {
    23  	fds, err := Parser{ImportPaths: []string{"../../internal/testprotos"}}.ParseFiles("desc_test_complex.proto")
    24  	testutil.Ok(t, err)
    25  
    26  	b, err := os.ReadFile("../../internal/testprotos/desc_test_complex.protoset")
    27  	testutil.Ok(t, err)
    28  
    29  	var fdSet descriptorpb.FileDescriptorSet
    30  	err = proto.Unmarshal(b, &fdSet)
    31  	testutil.Ok(t, err)
    32  
    33  	testutil.Require(t, proto.Equal(fdSet.File[0], protov1.MessageV2(fds[0].AsProto())), "linked descriptor did not match output from protoc:\nwanted: %s\ngot: %s", toString(fdSet.File[0]), toString(protov1.MessageV2(fds[0].AsProto())))
    34  }
    35  
    36  func TestMultiFileLink(t *testing.T) {
    37  	for _, name := range []string{"desc_test2.proto", "desc_test_defaults.proto", "desc_test_field_types.proto", "desc_test_options.proto", "desc_test_proto3.proto", "desc_test_wellknowntypes.proto"} {
    38  		fds, err := Parser{ImportPaths: []string{"../../internal/testprotos"}}.ParseFiles(name)
    39  		testutil.Ok(t, err)
    40  
    41  		exp, err := desc.LoadFileDescriptor(name)
    42  		testutil.Ok(t, err)
    43  
    44  		checkFiles(t, fds[0], exp, map[string]struct{}{})
    45  	}
    46  }
    47  
    48  func TestProto3Optional(t *testing.T) {
    49  	data, err := os.ReadFile("../../internal/testprotos/proto3_optional/desc_test_proto3_optional.protoset")
    50  	testutil.Ok(t, err)
    51  	var fdset descriptorpb.FileDescriptorSet
    52  	err = proto.Unmarshal(data, &fdset)
    53  	testutil.Ok(t, err)
    54  
    55  	var descriptorProto *descriptorpb.FileDescriptorProto
    56  	for _, fd := range fdset.File {
    57  		// not comparing source code info
    58  		fd.SourceCodeInfo = nil
    59  
    60  		// we want to use the same descriptor.proto as in protoset, so we don't have to
    61  		// worry about this test breaking when updating to newer versions of the Go
    62  		// descriptor package (which may have a different version of descriptor.proto
    63  		// compiled in).
    64  		if fd.GetName() == "google/protobuf/descriptor.proto" {
    65  			descriptorProto = fd
    66  		}
    67  	}
    68  	testutil.Require(t, descriptorProto != nil, "failed to find google/protobuf/descriptor.proto in protoset")
    69  
    70  	exp, err := desc.CreateFileDescriptorFromSet(&fdset)
    71  	testutil.Ok(t, err)
    72  
    73  	fds, err := Parser{
    74  		ImportPaths: []string{"../../internal/testprotos"},
    75  		LookupImportProto: func(name string) (*descriptorpb.FileDescriptorProto, error) {
    76  			if name == "google/protobuf/descriptor.proto" {
    77  				return descriptorProto, nil
    78  			}
    79  			return nil, errors.New("not found")
    80  		},
    81  	}.ParseFiles("proto3_optional/desc_test_proto3_optional.proto")
    82  	testutil.Ok(t, err)
    83  
    84  	checkFiles(t, fds[0], exp, map[string]struct{}{})
    85  }
    86  
    87  func checkFiles(t *testing.T, act, exp *desc.FileDescriptor, checked map[string]struct{}) {
    88  	if _, ok := checked[act.GetName()]; ok {
    89  		// already checked
    90  		return
    91  	}
    92  	checked[act.GetName()] = struct{}{}
    93  
    94  	// remove any source code info from expected value, since actual won't have any
    95  	exp.AsFileDescriptorProto().SourceCodeInfo = nil
    96  
    97  	testutil.Require(t, proto.Equal(exp.AsFileDescriptorProto(), protov1.MessageV2(act.AsProto())), "linked descriptor did not match output from protoc:\nwanted: %s\ngot: %s", toString(protov1.MessageV2(exp.AsProto())), toString(protov1.MessageV2(act.AsProto())))
    98  
    99  	for i, dep := range act.GetDependencies() {
   100  		checkFiles(t, dep, exp.GetDependencies()[i], checked)
   101  	}
   102  }
   103  
   104  func toString(m proto.Message) string {
   105  	msh := protojson.MarshalOptions{Indent: "  "}
   106  	data, err := msh.Marshal(m)
   107  	if err != nil {
   108  		panic(err)
   109  	}
   110  	return string(data)
   111  }
   112  
   113  func TestLinkerValidation(t *testing.T) {
   114  	testCases := []struct {
   115  		input  map[string]string
   116  		errMsg string
   117  	}{
   118  		{
   119  			map[string]string{
   120  				"foo.proto":  `syntax = "proto3"; package namespace.a; import "foo2.proto"; import "foo3.proto"; import "foo4.proto"; message Foo{ b.Bar a = 1; b.Baz b = 2; b.Buzz c = 3; }`,
   121  				"foo2.proto": `syntax = "proto3"; package namespace.b; message Bar{}`,
   122  				"foo3.proto": `syntax = "proto3"; package namespace.b; message Baz{}`,
   123  				"foo4.proto": `syntax = "proto3"; package namespace.b; message Buzz{}`,
   124  			},
   125  			"", // should succeed
   126  		},
   127  		{
   128  			map[string]string{
   129  				"foo.proto": "import \"foo2.proto\"; message fubar{}",
   130  			},
   131  			`foo.proto:1:8: file not found: foo2.proto`,
   132  		},
   133  		{
   134  			map[string]string{
   135  				"foo.proto":  "import \"foo2.proto\"; message fubar{}",
   136  				"foo2.proto": "import \"foo.proto\"; message baz{}",
   137  			},
   138  			`foo.proto:1:8: cycle found in imports: "foo.proto" -> "foo2.proto" -> "foo.proto"
   139  					|| foo2.proto:1:8: cycle found in imports: "foo2.proto" -> "foo.proto" -> "foo2.proto"`,
   140  		},
   141  		{
   142  			map[string]string{
   143  				"foo.proto": "enum foo { bar = 1; baz = 2; } enum fu { bar = 1; baz = 2; }",
   144  			},
   145  			`foo.proto:1:42: symbol "bar" already defined at foo.proto:1:12; protobuf uses C++ scoping rules for enum values, so they exist in the scope enclosing the enum`,
   146  		},
   147  		{
   148  			map[string]string{
   149  				"foo.proto": "message foo {} enum foo { V = 0; }",
   150  			},
   151  			`foo.proto:1:21: symbol "foo" already defined at foo.proto:1:9`,
   152  		},
   153  		{
   154  			map[string]string{
   155  				"foo.proto": "message foo { optional string a = 1; optional string a = 2; }",
   156  			},
   157  			`foo.proto:1:54: symbol "foo.a" already defined at foo.proto:1:31`,
   158  		},
   159  		{
   160  			map[string]string{
   161  				"foo.proto":  "message foo {}",
   162  				"foo2.proto": "enum foo { V = 0; }",
   163  			},
   164  			`foo.proto:1:9: symbol "foo" already defined at foo2.proto:1:6
   165  					|| foo2.proto:1:6: symbol "foo" already defined at foo.proto:1:9`,
   166  		},
   167  		{
   168  			map[string]string{
   169  				"foo.proto": "message foo { optional blah a = 1; }",
   170  			},
   171  			"foo.proto:1:24: field foo.a: unknown type blah",
   172  		},
   173  		{
   174  			map[string]string{
   175  				"foo.proto": "message foo { optional bar.baz a = 1; } service bar { rpc baz (foo) returns (foo); }",
   176  			},
   177  			"foo.proto:1:24: field foo.a: invalid type: bar.baz is a method, not a message or enum",
   178  		},
   179  		{
   180  			map[string]string{
   181  				"foo.proto": "message foo { extensions 1 to 2; } extend foo { optional string a = 1; } extend foo { optional int32 b = 1; }",
   182  			},
   183  			"foo.proto:1:106: extension with tag 1 for message foo already defined at foo.proto:1:69",
   184  		},
   185  		{
   186  			map[string]string{
   187  				"foo.proto": `
   188  					syntax = "proto3";
   189  					import "google/protobuf/descriptor.proto";
   190  					package google.protobuf;
   191  					message DescriptorProto { }
   192  				`,
   193  			},
   194  			`foo.proto:5:49: symbol "google.protobuf.DescriptorProto" already defined at google/protobuf/descriptor.proto`,
   195  		},
   196  		{
   197  			map[string]string{
   198  				"foo.proto": "package fu.baz; extend foobar { optional string a = 1; }",
   199  			},
   200  			"foo.proto:1:24: unknown extendee type foobar",
   201  		},
   202  		{
   203  			map[string]string{
   204  				"foo.proto": "package fu.baz; service foobar{} extend foobar { optional string a = 1; }",
   205  			},
   206  			"foo.proto:1:41: extendee is invalid: fu.baz.foobar is a service, not a message",
   207  		},
   208  		{
   209  			map[string]string{
   210  				"foo.proto": "message foo{} message bar{} service foobar{ rpc foo(foo) returns (bar); }",
   211  			},
   212  			"foo.proto:1:53: method foobar.foo: invalid request type: foobar.foo is a method, not a message",
   213  		},
   214  		{
   215  			map[string]string{
   216  				"foo.proto": "message foo{} message bar{} service foobar{ rpc foo(bar) returns (foo); }",
   217  			},
   218  			"foo.proto:1:67: method foobar.foo: invalid response type: foobar.foo is a method, not a message",
   219  		},
   220  		{
   221  			map[string]string{
   222  				"foo.proto": "package fu.baz; message foobar{ extensions 1; } extend foobar { optional string a = 2; }",
   223  			},
   224  			"foo.proto:1:85: extension fu.baz.a: tag 2 is not in valid range for extended type fu.baz.foobar",
   225  		},
   226  		{
   227  			map[string]string{
   228  				"foo.proto":  "package fu.baz; import public \"foo2.proto\"; message foobar{ optional baz a = 1; }",
   229  				"foo2.proto": "package fu.baz; import \"foo3.proto\"; message fizzle{ }",
   230  				"foo3.proto": "package fu.baz; message baz{ }",
   231  			},
   232  			"foo.proto:1:70: field fu.baz.foobar.a: unknown type baz; resolved to fu.baz which is not defined; consider using a leading dot",
   233  		},
   234  		{
   235  			map[string]string{
   236  				"foo.proto": `
   237  					syntax = "proto2";
   238  					package foo;
   239  					import "google/protobuf/descriptor.proto";
   240  					extend google.protobuf.FileOptions           { optional string fil_foo = 12000; }
   241  					extend google.protobuf.MessageOptions        { optional string msg_foo = 12000; }
   242  					extend google.protobuf.FieldOptions          { optional string fld_foo = 12000 [(fld_foo) = "extension"]; }
   243  					extend google.protobuf.OneofOptions          { optional string oof_foo = 12000; }
   244  					extend google.protobuf.EnumOptions           { optional string enm_foo = 12000; }
   245  					extend google.protobuf.EnumValueOptions      { optional string env_foo = 12000; }
   246  					extend google.protobuf.ExtensionRangeOptions { optional string ext_foo = 12000; }
   247  					extend google.protobuf.ServiceOptions        { optional string svc_foo = 12000; }
   248  					extend google.protobuf.MethodOptions         { optional string mtd_foo = 12000; }
   249  					option (fil_foo) = "file";
   250  					message Foo {
   251  						option (msg_foo) = "message";
   252  						oneof foo {
   253  							option (oof_foo) = "oneof";
   254  							string bar = 1 [(fld_foo) = "field"];
   255  						}
   256  						extensions 100 to 200 [(ext_foo) = "extensionrange"];
   257  					}
   258  					enum Baz {
   259  						option (enm_foo) = "enum";
   260  						ZERO = 0 [(env_foo) = "enumvalue"];
   261  					}
   262  					service FooService {
   263  						option (svc_foo) = "service";
   264  						rpc Bar(Foo) returns (Foo) {
   265  							option (mtd_foo) = "method";
   266  						}
   267  					}
   268  					`,
   269  			},
   270  			"", // should success
   271  		},
   272  		{
   273  			map[string]string{
   274  				"foo.proto": "package fu.baz; message foobar{ repeated string a = 1 [default = \"abc\"]; }",
   275  			},
   276  			"foo.proto:1:56: field fu.baz.foobar.a: default value cannot be set because field is repeated",
   277  		},
   278  		{
   279  			map[string]string{
   280  				"foo.proto": "package fu.baz; message foobar{ optional foobar a = 1 [default = { a: {} }]; }",
   281  			},
   282  			"foo.proto:1:56: field fu.baz.foobar.a: default value cannot be set because field is a message",
   283  		},
   284  		{
   285  			map[string]string{
   286  				"foo.proto": "package fu.baz; message foobar{ optional string a = 1 [default = { a: \"abc\" }]; }",
   287  			},
   288  			"foo.proto:1:66: field fu.baz.foobar.a: option default: default value cannot be a message",
   289  		},
   290  		{
   291  			map[string]string{
   292  				"foo.proto": "package fu.baz; message foobar{ optional string a = 1 [default = 1.234]; }",
   293  			},
   294  			"foo.proto:1:66: field fu.baz.foobar.a: option default: expecting string, got double",
   295  		},
   296  		{
   297  			map[string]string{
   298  				"foo.proto": "package fu.baz; enum abc { OK=0; NOK=1; } message foobar{ optional abc a = 1 [default = NACK]; }",
   299  			},
   300  			"foo.proto:1:89: field fu.baz.foobar.a: option default: enum fu.baz.abc has no value named NACK",
   301  		},
   302  		{
   303  			map[string]string{
   304  				"foo.proto": "option b = 123;",
   305  			},
   306  			"foo.proto:1:8: option b: field b of google.protobuf.FileOptions does not exist",
   307  		},
   308  		{
   309  			map[string]string{
   310  				"foo.proto": "option (foo.bar) = 123;",
   311  			},
   312  			"foo.proto:1:8: unknown extension foo.bar",
   313  		},
   314  		{
   315  			map[string]string{
   316  				"foo.proto": "option uninterpreted_option = { };",
   317  			},
   318  			"foo.proto:1:8: invalid option 'uninterpreted_option'",
   319  		},
   320  		{
   321  			map[string]string{
   322  				"foo.proto": "import \"google/protobuf/descriptor.proto\";\n" +
   323  					"message foo { optional string a = 1; extensions 10 to 20; }\n" +
   324  					"extend foo { optional int32 b = 10; }\n" +
   325  					"extend google.protobuf.FileOptions { optional foo f = 20000; }\n" +
   326  					"option (f).b = 123;",
   327  			},
   328  			"foo.proto:5:12: option (f).b: field b of foo does not exist",
   329  		},
   330  		{
   331  			map[string]string{
   332  				"foo.proto": "import \"google/protobuf/descriptor.proto\";\n" +
   333  					"message foo { optional string a = 1; extensions 10 to 20; }\n" +
   334  					"extend foo { optional int32 b = 10; }\n" +
   335  					"extend google.protobuf.FileOptions { optional foo f = 20000; }\n" +
   336  					"option (f).a = 123;",
   337  			},
   338  			"foo.proto:5:16: option (f).a: expecting string, got integer",
   339  		},
   340  		{
   341  			map[string]string{
   342  				"foo.proto": "import \"google/protobuf/descriptor.proto\";\n" +
   343  					"message foo { optional string a = 1; extensions 10 to 20; }\n" +
   344  					"extend foo { optional int32 b = 10; }\n" +
   345  					"extend google.protobuf.FileOptions { optional foo f = 20000; }\n" +
   346  					"option (b) = 123;",
   347  			},
   348  			"foo.proto:5:8: option (b): extension b should extend google.protobuf.FileOptions but instead extends foo",
   349  		},
   350  		{
   351  			map[string]string{
   352  				"foo.proto": "import \"google/protobuf/descriptor.proto\";\n" +
   353  					"message foo { optional string a = 1; extensions 10 to 20; }\n" +
   354  					"extend foo { optional int32 b = 10; }\n" +
   355  					"extend google.protobuf.FileOptions { optional foo f = 20000; }\n" +
   356  					"option (foo) = 123;",
   357  			},
   358  			"foo.proto:5:8: invalid extension: foo is a message, not an extension",
   359  		},
   360  		{
   361  			map[string]string{
   362  				"foo.proto": "import \"google/protobuf/descriptor.proto\";\n" +
   363  					"message foo { optional string a = 1; extensions 10 to 20; }\n" +
   364  					"extend foo { optional int32 b = 10; }\n" +
   365  					"extend google.protobuf.FileOptions { optional foo f = 20000; }\n" +
   366  					"option (foo.a) = 123;",
   367  			},
   368  			"foo.proto:5:8: invalid extension: foo.a is a field but not an extension",
   369  		},
   370  		{
   371  			map[string]string{
   372  				"foo.proto": "import \"google/protobuf/descriptor.proto\";\n" +
   373  					"message foo { optional string a = 1; extensions 10 to 20; }\n" +
   374  					"extend foo { optional int32 b = 10; }\n" +
   375  					"extend google.protobuf.FileOptions { optional foo f = 20000; }\n" +
   376  					"option (f) = { a: [ 123 ] };",
   377  			},
   378  			"foo.proto:5:19: option (f): value is an array but field is not repeated",
   379  		},
   380  		{
   381  			map[string]string{
   382  				"foo.proto": "import \"google/protobuf/descriptor.proto\";\n" +
   383  					"message foo { repeated string a = 1; extensions 10 to 20; }\n" +
   384  					"extend foo { optional int32 b = 10; }\n" +
   385  					"extend google.protobuf.FileOptions { optional foo f = 20000; }\n" +
   386  					"option (f) = { a: [ \"a\", \"b\", 123 ] };",
   387  			},
   388  			"foo.proto:5:31: option (f): expecting string, got integer",
   389  		},
   390  		{
   391  			map[string]string{
   392  				"foo.proto": "import \"google/protobuf/descriptor.proto\";\n" +
   393  					"message foo { optional string a = 1; extensions 10 to 20; }\n" +
   394  					"extend foo { optional int32 b = 10; }\n" +
   395  					"extend google.protobuf.FileOptions { optional foo f = 20000; }\n" +
   396  					"option (f) = { a: \"a\" };\n" +
   397  					"option (f) = { a: \"b\" };",
   398  			},
   399  			"foo.proto:6:8: option (f): non-repeated option field (f) already set",
   400  		},
   401  		{
   402  			map[string]string{
   403  				"foo.proto": "import \"google/protobuf/descriptor.proto\";\n" +
   404  					"message foo { optional string a = 1; extensions 10 to 20; }\n" +
   405  					"extend foo { optional int32 b = 10; }\n" +
   406  					"extend google.protobuf.FileOptions { optional foo f = 20000; }\n" +
   407  					"option (f) = { a: \"a\" };\n" +
   408  					"option (f).a = \"b\";",
   409  			},
   410  			"foo.proto:6:12: option (f).a: non-repeated option field a already set",
   411  		},
   412  		{
   413  			map[string]string{
   414  				"foo.proto": "import \"google/protobuf/descriptor.proto\";\n" +
   415  					"message foo { optional string a = 1; extensions 10 to 20; }\n" +
   416  					"extend foo { optional int32 b = 10; }\n" +
   417  					"extend google.protobuf.FileOptions { optional foo f = 20000; }\n" +
   418  					"option (f) = { a: \"a\" };\n" +
   419  					"option (f).(b) = \"b\";",
   420  			},
   421  			"foo.proto:6:18: option (f).(b): expecting int32, got string",
   422  		},
   423  		{
   424  			map[string]string{
   425  				"foo.proto": "import \"google/protobuf/descriptor.proto\";\n" +
   426  					"message foo { required string a = 1; required string b = 2; }\n" +
   427  					"extend google.protobuf.FileOptions { optional foo f = 20000; }\n" +
   428  					"option (f) = { a: \"a\" };\n",
   429  			},
   430  			"foo.proto:1:1: error in file options: some required fields missing: (f).b",
   431  		},
   432  		{
   433  			map[string]string{
   434  				"foo.proto": "message Foo { option message_set_wire_format = true; extensions 1 to 100; } extend Foo { optional int32 bar = 1; }",
   435  			},
   436  			"foo.proto:1:99: messages with message-set wire format cannot contain scalar extensions, only messages",
   437  		},
   438  		{
   439  			map[string]string{
   440  				"foo.proto": "message Foo { option message_set_wire_format = true; extensions 1 to 100; } extend Foo { optional Foo bar = 1; }",
   441  			},
   442  			"", // should succeed
   443  		},
   444  		{
   445  			map[string]string{
   446  				"foo.proto": "message Foo { option message_set_wire_format = true; extensions 1 to 100; } extend Foo { repeated Foo bar = 1; }",
   447  			},
   448  			"foo.proto:1:90: messages with message-set wire format cannot contain repeated extensions, only optional",
   449  		},
   450  		{
   451  			map[string]string{
   452  				"foo.proto": "message Foo { extensions 1 to max; } extend Foo { optional int32 bar = 536870912; }",
   453  			},
   454  			"foo.proto:1:72: extension bar: tag 536870912 is not in valid range for extended type Foo",
   455  		},
   456  		{
   457  			map[string]string{
   458  				"foo.proto": "message Foo { option message_set_wire_format = true; extensions 1 to max; } extend Foo { optional Foo bar = 536870912; }",
   459  			},
   460  			"", // should succeed
   461  		},
   462  		{
   463  			map[string]string{
   464  				"foo.proto": `syntax = "proto3"; package com.google; import "google/protobuf/wrappers.proto"; message Foo { google.protobuf.StringValue str = 1; }`,
   465  			},
   466  			"foo.proto:1:95: field com.google.Foo.str: unknown type google.protobuf.StringValue; resolved to com.google.protobuf.StringValue which is not defined; consider using a leading dot",
   467  		},
   468  		{
   469  			map[string]string{
   470  				"foo.proto": "syntax = \"proto2\";\n" +
   471  					"import \"google/protobuf/descriptor.proto\";\n" +
   472  					"message Foo {\n" +
   473  					"  optional group Bar = 1 { optional string name = 1; }\n" +
   474  					"}\n" +
   475  					"extend google.protobuf.MessageOptions { optional Foo foo = 10001; }\n" +
   476  					"message Baz { option (foo).bar.name = \"abc\"; }\n",
   477  			},
   478  			"", // should succeed
   479  		},
   480  		{
   481  			map[string]string{
   482  				"foo.proto": "syntax = \"proto2\";\n" +
   483  					"import \"google/protobuf/descriptor.proto\";\n" +
   484  					"message Foo {\n" +
   485  					"  optional group Bar = 1 { optional string name = 1; }\n" +
   486  					"}\n" +
   487  					"extend google.protobuf.MessageOptions { optional Foo foo = 10001; }\n" +
   488  					"message Baz { option (foo).Bar.name = \"abc\"; }\n",
   489  			},
   490  			"foo.proto:7:28: message Baz: option (foo).Bar.name: field Bar of Foo does not exist",
   491  		},
   492  		{
   493  			map[string]string{
   494  				"foo.proto": "syntax = \"proto2\";\n" +
   495  					"import \"google/protobuf/descriptor.proto\";\n" +
   496  					"extend google.protobuf.MessageOptions {\n" +
   497  					"  optional group Foo = 10001 { optional string name = 1; }\n" +
   498  					"}\n" +
   499  					"message Bar { option (foo).name = \"abc\"; }\n",
   500  			},
   501  			"", // should succeed
   502  		},
   503  		{
   504  			map[string]string{
   505  				"foo.proto": "syntax = \"proto2\";\n" +
   506  					"import \"google/protobuf/descriptor.proto\";\n" +
   507  					"extend google.protobuf.MessageOptions {\n" +
   508  					"  optional group Foo = 10001 { optional string name = 1; }\n" +
   509  					"}\n" +
   510  					"message Bar { option (Foo).name = \"abc\"; }\n",
   511  			},
   512  			"foo.proto:6:22: message Bar: invalid extension: Foo is a message, not an extension",
   513  		},
   514  		{
   515  			map[string]string{
   516  				"foo.proto": "syntax = \"proto2\";\n" +
   517  					"import \"google/protobuf/descriptor.proto\";\n" +
   518  					"message Foo {\n" +
   519  					"  optional group Bar = 1 { optional string name = 1; }\n" +
   520  					"}\n" +
   521  					"extend google.protobuf.MessageOptions { optional Foo foo = 10001; }\n" +
   522  					"message Baz { option (foo) = { Bar< name: \"abc\" > }; }\n",
   523  			},
   524  			"", // should succeed
   525  		},
   526  		{
   527  			map[string]string{
   528  				"foo.proto": "syntax = \"proto2\";\n" +
   529  					"import \"google/protobuf/descriptor.proto\";\n" +
   530  					"message Foo {\n" +
   531  					"  optional group Bar = 1 { optional string name = 1; }\n" +
   532  					"}\n" +
   533  					"extend google.protobuf.MessageOptions { optional Foo foo = 10001; }\n" +
   534  					"message Baz { option (foo) = { bar< name: \"abc\" > }; }\n",
   535  			},
   536  			"foo.proto:7:32: message Baz: option (foo): field bar not found (did you mean the group named Bar?)",
   537  		},
   538  		{
   539  			map[string]string{
   540  				"foo.proto": "syntax = \"proto2\";\n" +
   541  					"import \"google/protobuf/descriptor.proto\";\n" +
   542  					"message Foo { extensions 1 to 10; }\n" +
   543  					"extend Foo { optional group Bar = 10 { optional string name = 1; } }\n" +
   544  					"extend google.protobuf.MessageOptions { optional Foo foo = 10001; }\n" +
   545  					"message Baz { option (foo) = { [bar]< name: \"abc\" > }; }\n",
   546  			},
   547  			"", // should succeed
   548  		},
   549  		{
   550  			map[string]string{
   551  				"foo.proto": "syntax = \"proto2\";\n" +
   552  					"import \"google/protobuf/descriptor.proto\";\n" +
   553  					"message Foo { extensions 1 to 10; }\n" +
   554  					"extend Foo { optional group Bar = 10 { optional string name = 1; } }\n" +
   555  					"extend google.protobuf.MessageOptions { optional Foo foo = 10001; }\n" +
   556  					"message Baz { option (foo) = { [Bar]< name: \"abc\" > }; }\n",
   557  			},
   558  			"foo.proto:6:33: message Baz: option (foo): invalid extension: Bar is a message, not an extension",
   559  		},
   560  		{
   561  			map[string]string{
   562  				"foo.proto": "syntax = \"proto3\";\n" +
   563  					"import \"google/protobuf/descriptor.proto\";\n" +
   564  					"message Foo { oneof bar { string baz = 1; string buzz = 2; } }\n" +
   565  					"extend google.protobuf.MessageOptions { optional Foo foo = 10001; }\n" +
   566  					"message Baz { option (foo) = { baz: \"abc\" buzz: \"xyz\" }; }\n",
   567  			},
   568  			`foo.proto:5:43: message Baz: option (foo): oneof "bar" already has field "baz" set`,
   569  		},
   570  		{
   571  			map[string]string{
   572  				"foo.proto": "syntax = \"proto3\";\n" +
   573  					"import \"google/protobuf/descriptor.proto\";\n" +
   574  					"message Foo { oneof bar { string baz = 1; string buzz = 2; } }\n" +
   575  					"extend google.protobuf.MessageOptions { optional Foo foo = 10001; }\n" +
   576  					"message Baz {\n" +
   577  					"  option (foo).baz = \"abc\";\n" +
   578  					"  option (foo).buzz = \"xyz\";\n" +
   579  					"}",
   580  			},
   581  			`foo.proto:7:16: message Baz: option (foo).buzz: oneof "bar" already has field "baz" set`,
   582  		},
   583  		{
   584  			map[string]string{
   585  				"a.proto": "syntax = \"proto3\";\n" +
   586  					"message m{\n" +
   587  					"  oneof z{\n" +
   588  					"    int64 z=1;\n" +
   589  					"  }\n" +
   590  					"}",
   591  			},
   592  			`a.proto:4:11: symbol "m.z" already defined at a.proto:3:9`,
   593  		},
   594  		{
   595  			map[string]string{
   596  				"a.proto": "syntax=\"proto3\";\n" +
   597  					"message m{\n" +
   598  					"  string z = 1;\n" +
   599  					"  oneof z{int64 b=2;}\n" +
   600  					"}",
   601  			},
   602  			`a.proto:4:9: symbol "m.z" already defined at a.proto:3:10`,
   603  		},
   604  		{
   605  			map[string]string{
   606  				"test.proto": "syntax=\"proto2\";\n" +
   607  					"package foo.bar;\n" +
   608  					"import \"google/protobuf/descriptor.proto\";\n" +
   609  					"message a { extensions 1 to 100; }\n" +
   610  					"extend google.protobuf.MessageOptions { optional a msga = 10000; }\n" +
   611  					"message b {\n" +
   612  					"  message c {\n" +
   613  					"    extend a { repeated int32 i = 1; repeated float f = 2; }\n" +
   614  					"  }\n" +
   615  					"  option (msga) = {\n" +
   616  					"    [foo.bar.b.c.i]: 123\n" +
   617  					"    [bar.b.c.i]: 234\n" +
   618  					"    [b.c.i]: 345\n" +
   619  					"  };\n" +
   620  					"  option (msga).(foo.bar.b.c.f) = 1.23;\n" +
   621  					"  option (msga).(bar.b.c.f) = 2.34;\n" +
   622  					"  option (msga).(b.c.f) = 3.45;\n" +
   623  					"}",
   624  			},
   625  			"", // should succeed
   626  		},
   627  		{
   628  			map[string]string{
   629  				"test.proto": "syntax=\"proto2\";\n" +
   630  					"package foo.bar;\n" +
   631  					"import \"google/protobuf/descriptor.proto\";\n" +
   632  					"message a { extensions 1 to 100; }\n" +
   633  					"message b { extensions 1 to 100; }\n" +
   634  					"extend google.protobuf.MessageOptions { optional a msga = 10000; }\n" +
   635  					"message c {\n" +
   636  					"  extend a { optional b b = 1; }\n" +
   637  					"  extend foo.bar.b { repeated int32 i = 1; repeated float f = 2; }\n" +
   638  					"  option (msga) = {\n" +
   639  					"    [foo.bar.c.b] {\n" +
   640  					"      [foo.bar.c.i]: 123\n" +
   641  					"      [bar.c.i]: 234\n" +
   642  					"      [c.i]: 345\n" +
   643  					"    }\n" +
   644  					"  };\n" +
   645  					"  option (msga).(foo.bar.c.b).(foo.bar.c.f) = 1.23;\n" +
   646  					"  option (msga).(foo.bar.c.b).(bar.c.f) = 2.34;\n" +
   647  					"  option (msga).(foo.bar.c.b).(c.f) = 3.45;\n" +
   648  					"}",
   649  			},
   650  			"", // should succeed
   651  		},
   652  		{
   653  			map[string]string{
   654  				"test.proto": "syntax=\"proto2\";\n" +
   655  					"package foo.bar;\n" +
   656  					"import \"google/protobuf/descriptor.proto\";\n" +
   657  					"message a { extensions 1 to 100; }\n" +
   658  					"extend google.protobuf.MessageOptions { optional a msga = 10000; }\n" +
   659  					"message b {\n" +
   660  					"  message c {\n" +
   661  					"    extend a { repeated int32 i = 1; repeated float f = 2; }\n" +
   662  					"  }\n" +
   663  					"  option (msga) = {\n" +
   664  					"    [c.i]: 456\n" +
   665  					"  };\n" +
   666  					"}",
   667  			},
   668  			"test.proto:11:6: message foo.bar.b: option (foo.bar.msga): unknown extension c.i",
   669  		},
   670  		{
   671  			map[string]string{
   672  				"test.proto": "syntax=\"proto2\";\n" +
   673  					"package foo.bar;\n" +
   674  					"import \"google/protobuf/descriptor.proto\";\n" +
   675  					"message a { extensions 1 to 100; }\n" +
   676  					"extend google.protobuf.MessageOptions { optional a msga = 10000; }\n" +
   677  					"message b {\n" +
   678  					"  message c {\n" +
   679  					"    extend a { repeated int32 i = 1; repeated float f = 2; }\n" +
   680  					"  }\n" +
   681  					"  option (msga) = {\n" +
   682  					"    [i]: 567\n" +
   683  					"  };\n" +
   684  					"}",
   685  			},
   686  			"test.proto:11:6: message foo.bar.b: option (foo.bar.msga): unknown extension i",
   687  		},
   688  		{
   689  			map[string]string{
   690  				"test.proto": "syntax=\"proto2\";\n" +
   691  					"package foo.bar;\n" +
   692  					"import \"google/protobuf/descriptor.proto\";\n" +
   693  					"message a { extensions 1 to 100; }\n" +
   694  					"extend google.protobuf.MessageOptions { optional a msga = 10000; }\n" +
   695  					"message b {\n" +
   696  					"  message c {\n" +
   697  					"    extend a { repeated int32 i = 1; repeated float f = 2; }\n" +
   698  					"  }\n" +
   699  					"  option (msga).(c.f) = 4.56;\n" +
   700  					"}",
   701  			},
   702  			"test.proto:10:17: message foo.bar.b: unknown extension c.f",
   703  		},
   704  		{
   705  			map[string]string{
   706  				"test.proto": "syntax=\"proto2\";\n" +
   707  					"package foo.bar;\n" +
   708  					"import \"google/protobuf/descriptor.proto\";\n" +
   709  					"message a { extensions 1 to 100; }\n" +
   710  					"extend google.protobuf.MessageOptions { optional a msga = 10000; }\n" +
   711  					"message b {\n" +
   712  					"  message c {\n" +
   713  					"    extend a { repeated int32 i = 1; repeated float f = 2; }\n" +
   714  					"  }\n" +
   715  					"  option (msga).(f) = 5.67;\n" +
   716  					"}",
   717  			},
   718  			"test.proto:10:17: message foo.bar.b: unknown extension f",
   719  		},
   720  		{
   721  			map[string]string{
   722  				"a.proto": "syntax=\"proto3\";\nmessage m{\n" +
   723  					"  oneof z{int64 a=1;}\n" +
   724  					"  oneof z{int64 b=2;}\n" +
   725  					"}",
   726  			},
   727  			`a.proto:4:9: symbol "m.z" already defined at a.proto:3:9`,
   728  		},
   729  		{
   730  			map[string]string{
   731  				"foo.proto": "syntax = \"proto3\";\n" +
   732  					"import \"google/protobuf/descriptor.proto\";\n" +
   733  					"message Foo { oneof bar { google.protobuf.DescriptorProto baz = 1; google.protobuf.DescriptorProto buzz = 2; } }\n" +
   734  					"extend google.protobuf.MessageOptions { optional Foo foo = 10001; }\n" +
   735  					"message Baz {\n" +
   736  					"  option (foo).baz.name = \"abc\";\n" +
   737  					"  option (foo).buzz.name = \"xyz\";\n" +
   738  					"}",
   739  			},
   740  			`foo.proto:7:16: message Baz: option (foo).buzz.name: oneof "bar" already has field "baz" set`,
   741  		},
   742  		{
   743  			map[string]string{
   744  				"foo.proto": "syntax = \"proto3\";\n" +
   745  					"import \"google/protobuf/descriptor.proto\";\n" +
   746  					"message Foo { oneof bar { google.protobuf.DescriptorProto baz = 1; google.protobuf.DescriptorProto buzz = 2; } }\n" +
   747  					"extend google.protobuf.MessageOptions { optional Foo foo = 10001; }\n" +
   748  					"message Baz {\n" +
   749  					"  option (foo).baz.options.(foo).baz.name = \"abc\";\n" +
   750  					"  option (foo).baz.options.(foo).buzz.name = \"xyz\";\n" +
   751  					"}",
   752  			},
   753  			`foo.proto:7:34: message Baz: option (foo).baz.options.(foo).buzz.name: oneof "bar" already has field "baz" set`,
   754  		},
   755  		{
   756  			map[string]string{
   757  				"foo.proto": "syntax = \"proto3\";\n" +
   758  					"import \"google/protobuf/descriptor.proto\";\n" +
   759  					"enum Foo { option allow_alias = true; true = 0; false = 1; True = 0; False = 1; t = 2; f = 3; inf = 4; nan = 5; }\n" +
   760  					"extend google.protobuf.MessageOptions { repeated Foo foo = 10001; }\n" +
   761  					"message Baz {\n" +
   762  					"  option (foo) = true; option (foo) = false;\n" +
   763  					"  option (foo) = t; option (foo) = f;\n" +
   764  					"  option (foo) = True; option (foo) = False;\n" +
   765  					"  option (foo) = inf; option (foo) = nan;\n" +
   766  					"}\n",
   767  			},
   768  			"", // should succeed
   769  		},
   770  		{
   771  			map[string]string{
   772  				"foo.proto": "syntax = \"proto3\";\n" +
   773  					"import \"google/protobuf/descriptor.proto\";\n" +
   774  					"extend google.protobuf.MessageOptions { repeated bool foo = 10001; }\n" +
   775  					"message Baz {\n" +
   776  					"  option (foo) = true; option (foo) = false;\n" +
   777  					"  option (foo) = t; option (foo) = f;\n" +
   778  					"  option (foo) = True; option (foo) = False;\n" +
   779  					"}\n",
   780  			},
   781  			"foo.proto:6:18: message Baz: option (foo): expecting bool, got identifier",
   782  		},
   783  		{
   784  			map[string]string{
   785  				"foo.proto": "syntax = \"proto3\";\n" +
   786  					"import \"google/protobuf/descriptor.proto\";\n" +
   787  					"message Foo { repeated bool b = 1; }\n" +
   788  					"extend google.protobuf.MessageOptions { Foo foo = 10001; }\n" +
   789  					"message Baz {\n" +
   790  					"  option (foo) = {\n" +
   791  					"    b: t     b: f\n" +
   792  					"    b: true  b: false\n" +
   793  					"    b: True  b: False\n" +
   794  					"  };\n" +
   795  					"}\n",
   796  			},
   797  			"", // should succeed
   798  		},
   799  		{
   800  			map[string]string{
   801  				"foo.proto": "syntax = \"proto2\";\n" +
   802  					"import \"google/protobuf/descriptor.proto\";\n" +
   803  					"message Foo { extensions 1 to 10; }\n" +
   804  					"extend Foo { optional bool b = 10; }\n" +
   805  					"extend google.protobuf.MessageOptions { optional Foo foo = 10001; }\n" +
   806  					"message Baz {\n" +
   807  					"  option (foo) = {\n" +
   808  					"    [.b]: true\n" +
   809  					"  };\n" +
   810  					"}\n",
   811  			},
   812  			"foo.proto:8:6: syntax error: unexpected '.'",
   813  		},
   814  		{
   815  			map[string]string{
   816  				"foo.proto": "syntax = \"proto3\";\n" +
   817  					"package foo.bar;\n" +
   818  					"import \"google/protobuf/any.proto\";\n" +
   819  					"import \"google/protobuf/descriptor.proto\";\n" +
   820  					"message Foo { string a = 1; int32 b = 2; }\n" +
   821  					"extend google.protobuf.MessageOptions { optional google.protobuf.Any any = 10001; }\n" +
   822  					"message Baz {\n" +
   823  					"  option (any) = {\n" +
   824  					"    [type.googleapis.com/foo.bar.Foo] <\n" +
   825  					"      a: \"abc\"\n" +
   826  					"      b: 123\n" +
   827  					"    >\n" +
   828  					"  };\n" +
   829  					"}\n",
   830  			},
   831  			"", // should succeed
   832  		},
   833  		{
   834  			map[string]string{
   835  				"foo.proto": "syntax = \"proto3\";\n" +
   836  					"package foo.bar;\n" +
   837  					"import \"google/protobuf/descriptor.proto\";\n" +
   838  					"message Foo { string a = 1; int32 b = 2; }\n" +
   839  					"extend google.protobuf.MessageOptions { optional Foo f = 10001; }\n" +
   840  					"message Baz {\n" +
   841  					"  option (f) = {\n" +
   842  					"    [type.googleapis.com/foo.bar.Foo] <\n" +
   843  					"      a: \"abc\"\n" +
   844  					"      b: 123\n" +
   845  					"    >\n" +
   846  					"  };\n" +
   847  					"}\n",
   848  			},
   849  			"foo.proto:8:6: message foo.bar.Baz: option (foo.bar.f): type references are only allowed for google.protobuf.Any, but this type is foo.bar.Foo",
   850  		},
   851  		{
   852  			map[string]string{
   853  				"foo.proto": "syntax = \"proto3\";\n" +
   854  					"package foo.bar;\n" +
   855  					"import \"google/protobuf/any.proto\";\n" +
   856  					"import \"google/protobuf/descriptor.proto\";\n" +
   857  					"message Foo { string a = 1; int32 b = 2; }\n" +
   858  					"extend google.protobuf.MessageOptions { optional google.protobuf.Any any = 10001; }\n" +
   859  					"message Baz {\n" +
   860  					"  option (any) = {\n" +
   861  					"    [types.custom.io/foo.bar.Foo] <\n" +
   862  					"      a: \"abc\"\n" +
   863  					"      b: 123\n" +
   864  					"    >\n" +
   865  					"  };\n" +
   866  					"}\n",
   867  			},
   868  			"foo.proto:9:6: message foo.bar.Baz: option (foo.bar.any): could not resolve type reference types.custom.io/foo.bar.Foo",
   869  		},
   870  		{
   871  			map[string]string{
   872  				"foo.proto": "syntax = \"proto3\";\n" +
   873  					"package foo.bar;\n" +
   874  					"import \"google/protobuf/any.proto\";\n" +
   875  					"import \"google/protobuf/descriptor.proto\";\n" +
   876  					"message Foo { string a = 1; int32 b = 2; }\n" +
   877  					"extend google.protobuf.MessageOptions { optional google.protobuf.Any any = 10001; }\n" +
   878  					"message Baz {\n" +
   879  					"  option (any) = {\n" +
   880  					"    [type.googleapis.com/foo.bar.Foo]: 123\n" +
   881  					"  };\n" +
   882  					"}\n",
   883  			},
   884  			"foo.proto:9:40: message foo.bar.Baz: option (foo.bar.any): type references for google.protobuf.Any must have message literal value",
   885  		},
   886  		{
   887  			map[string]string{
   888  				"foo.proto": "syntax = \"proto3\";\n" +
   889  					"package foo.bar;\n" +
   890  					"import \"google/protobuf/any.proto\";\n" +
   891  					"import \"google/protobuf/descriptor.proto\";\n" +
   892  					"message Foo { string a = 1; int32 b = 2; }\n" +
   893  					"extend google.protobuf.MessageOptions { optional google.protobuf.Any any = 10001; }\n" +
   894  					"message Baz {\n" +
   895  					"  option (any) = {\n" +
   896  					"    [type.googleapis.com/Foo] <\n" +
   897  					"      a: \"abc\"\n" +
   898  					"      b: 123\n" +
   899  					"    >\n" +
   900  					"  };\n" +
   901  					"}\n",
   902  			},
   903  			"foo.proto:9:6: message foo.bar.Baz: option (foo.bar.any): could not resolve type reference type.googleapis.com/Foo",
   904  		},
   905  		{
   906  			map[string]string{
   907  				"foo.proto": "syntax = \"proto3\";\n" +
   908  					"import \"google/protobuf/descriptor.proto\";\n" +
   909  					"extend google.protobuf.MessageOptions {\n" +
   910  					"  string foobar = 10001 [json_name=\"FooBar\"];\n" +
   911  					"}\n",
   912  			},
   913  			"foo.proto:4:26: field foobar: option json_name is not allowed on extensions",
   914  		},
   915  		{
   916  			map[string]string{
   917  				"foo.proto": "syntax = \"proto3\";\n" +
   918  					"package foo.foo;\n" +
   919  					"import \"other.proto\";\n" +
   920  					"service Foo { rpc Bar (Baz) returns (Baz); }\n" +
   921  					"message Baz {\n" +
   922  					"  foo.Foo.Bar f = 1;\n" +
   923  					"}\n",
   924  				"other.proto": "syntax = \"proto3\";\n" +
   925  					"package foo;\n" +
   926  					"message Foo {\n" +
   927  					"  enum Bar { ZED = 0; }\n" +
   928  					"}\n",
   929  			},
   930  			"foo.proto:6:3: field foo.foo.Baz.f: invalid type: foo.foo.Foo.Bar is a method, not a message or enum",
   931  		},
   932  		{
   933  			map[string]string{
   934  				"foo.proto": "syntax = \"proto3\";\n" +
   935  					"import \"google/protobuf/descriptor.proto\";\n" +
   936  					"message Foo {\n" +
   937  					"  enum Bar { ZED = 0; }\n" +
   938  					"  message Foo {\n" +
   939  					"    extend google.protobuf.MessageOptions {\n" +
   940  					"      string Bar = 30000;\n" +
   941  					"    }\n" +
   942  					"    Foo.Bar f = 1;\n" +
   943  					"  }\n" +
   944  					"}\n",
   945  			},
   946  			"foo.proto:9:5: field Foo.Foo.f: invalid type: Foo.Foo.Bar is an extension, not a message or enum",
   947  		},
   948  		{
   949  			map[string]string{
   950  				"foo.proto": "syntax = \"proto3\";\n" +
   951  					"import \"google/protobuf/descriptor.proto\";\n" +
   952  					"extend google.protobuf.ServiceOptions {\n" +
   953  					"  string Bar = 30000;\n" +
   954  					"}\n" +
   955  					"message Empty {}\n" +
   956  					"service Foo {\n" +
   957  					"  option (Bar) = \"blah\";\n" +
   958  					"  rpc Bar (Empty) returns (Empty);\n" +
   959  					"}\n",
   960  			},
   961  			"", // should succeed
   962  		},
   963  		{
   964  			map[string]string{
   965  				"foo.proto": "syntax = \"proto3\";\n" +
   966  					"import \"google/protobuf/descriptor.proto\";\n" +
   967  					"extend google.protobuf.MethodOptions {\n" +
   968  					"  string Bar = 30000;\n" +
   969  					"}\n" +
   970  					"message Empty {}\n" +
   971  					"service Foo {\n" +
   972  					"  rpc Bar (Empty) returns (Empty) { option (Bar) = \"blah\"; }\n" +
   973  					"}\n",
   974  			},
   975  			"foo.proto:8:44: method Foo.Bar: invalid extension: Bar is a method, not an extension",
   976  		},
   977  		{
   978  			map[string]string{
   979  				"foo.proto": "syntax = \"proto3\";\n" +
   980  					"import \"google/protobuf/descriptor.proto\";\n" +
   981  					"enum Bar { ZED = 0; }\n" +
   982  					"message Foo {\n" +
   983  					"  extend google.protobuf.MessageOptions {\n" +
   984  					"    string Bar = 30000;\n" +
   985  					"  }\n" +
   986  					"  message Foo {\n" +
   987  					"    Bar f = 1;\n" +
   988  					"  }\n" +
   989  					"}\n",
   990  			},
   991  			"", // should succeed
   992  		},
   993  		{
   994  			map[string]string{
   995  				"foo.proto": "syntax = \"proto3\";\n" +
   996  					"message Foo {\n" +
   997  					"  map<string,string> bar = 1;\n" +
   998  					"}\n" +
   999  					"message Baz {\n" +
  1000  					"  Foo.BarEntry e = 1;\n" +
  1001  					"}\n",
  1002  			},
  1003  			"foo.proto:6:3: field Baz.e: Foo.BarEntry is a synthetic map entry and may not be referenced explicitly",
  1004  		},
  1005  		{
  1006  			map[string]string{
  1007  				"foo.proto": "syntax = \"proto3\";\n" +
  1008  					"import \"google/protobuf/struct.proto\";\n" +
  1009  					"message Foo {\n" +
  1010  					"  google.protobuf.Struct.FieldsEntry e = 1;\n" +
  1011  					"}\n",
  1012  			},
  1013  			"foo.proto:4:3: field Foo.e: google.protobuf.Struct.FieldsEntry is a synthetic map entry and may not be referenced explicitly",
  1014  		},
  1015  		{
  1016  			map[string]string{
  1017  				"foo.proto": "syntax = \"proto3\";\n" +
  1018  					"message Foo {\n" +
  1019  					"  string foo = 1;\n" +
  1020  					"  string bar = 2 [json_name=\"foo\"];\n" +
  1021  					"}\n",
  1022  			},
  1023  			"foo.proto:4:3: field Foo.bar: custom JSON name \"foo\" conflicts with default JSON name of field foo, defined at foo.proto:3:3",
  1024  		},
  1025  		{
  1026  			map[string]string{
  1027  				"foo.proto": "syntax = \"proto3\";\n" +
  1028  					"message Blah {\n" +
  1029  					"  message Foo {\n" +
  1030  					"    string foo = 1;\n" +
  1031  					"    string bar = 2 [json_name=\"foo\"];\n" +
  1032  					"  }\n" +
  1033  					"}\n",
  1034  			},
  1035  			"foo.proto:5:5: field Foo.bar: custom JSON name \"foo\" conflicts with default JSON name of field foo, defined at foo.proto:4:5",
  1036  		}, {
  1037  			map[string]string{
  1038  				"foo.proto": "syntax = \"proto3\";\n" +
  1039  					"message Foo {\n" +
  1040  					"  string foo = 1 [json_name=\"foo_bar\"];\n" +
  1041  					"  string bar = 2 [json_name=\"Foo_Bar\"];\n" +
  1042  					"}\n",
  1043  			},
  1044  			"", // should succeed
  1045  		},
  1046  		{
  1047  			map[string]string{
  1048  				"foo.proto": "syntax = \"proto3\";\n" +
  1049  					"message Foo {\n" +
  1050  					"  string fooBar = 1;\n" +
  1051  					"  string foo_bar = 2;\n" +
  1052  					"}\n",
  1053  			},
  1054  			"foo.proto:4:3: field Foo.foo_bar: default JSON name \"fooBar\" conflicts with default JSON name of field fooBar, defined at foo.proto:3:3",
  1055  		},
  1056  		{
  1057  			map[string]string{
  1058  				"foo.proto": "syntax = \"proto3\";\n" +
  1059  					"message Foo {\n" +
  1060  					"  string fooBar = 1;\n" +
  1061  					"  string foo_bar = 2 [json_name=\"fuber\"];\n" +
  1062  					"}\n",
  1063  			},
  1064  			"foo.proto:4:3: field Foo.foo_bar: default JSON name \"fooBar\" conflicts with default JSON name of field fooBar, defined at foo.proto:3:3",
  1065  		}, {
  1066  			map[string]string{
  1067  				"foo.proto": "syntax = \"proto3\";\n" +
  1068  					"message Foo {\n" +
  1069  					"  string fooBar = 1;\n" +
  1070  					"  string FOO_BAR = 2;\n" +
  1071  					"}\n",
  1072  			},
  1073  			"", // should succeed
  1074  		},
  1075  		{
  1076  			map[string]string{
  1077  				"foo.proto": "syntax = \"proto3\";\n" +
  1078  					"message Foo {\n" +
  1079  					"  string fooBar = 1;\n" +
  1080  					"  string __foo_bar = 2;\n" +
  1081  					"}\n",
  1082  			},
  1083  			"", // should succeed
  1084  		},
  1085  		{
  1086  			map[string]string{
  1087  				"foo.proto": "syntax = \"proto2\";\n" +
  1088  					"message Foo {\n" +
  1089  					"  optional string foo = 1 [json_name=\"foo_bar\"];\n" +
  1090  					"  optional string bar = 2 [json_name=\"Foo_Bar\"];\n" +
  1091  					"}\n",
  1092  			},
  1093  			"", // should succeed
  1094  		},
  1095  		{
  1096  			map[string]string{
  1097  				"foo.proto": "syntax = \"proto2\";\n" +
  1098  					"message Blah {\n" +
  1099  					"  message Foo {\n" +
  1100  					"    optional string foo = 1 [json_name=\"foo_bar\"];\n" +
  1101  					"    optional string bar = 2 [json_name=\"Foo_Bar\"];\n" +
  1102  					"  }\n" +
  1103  					"}\n",
  1104  			},
  1105  			"", // should succeed
  1106  		},
  1107  		{
  1108  			map[string]string{
  1109  				"foo.proto": "syntax = \"proto2\";\n" +
  1110  					"message Foo {\n" +
  1111  					"  optional string fooBar = 1;\n" +
  1112  					"  optional string foo_bar = 2;\n" +
  1113  					"}\n",
  1114  			},
  1115  			"", // should succeed: only check default JSON names in proto3
  1116  		},
  1117  		{
  1118  			map[string]string{
  1119  				"foo.proto": "syntax = \"proto2\";\n" +
  1120  					"message Foo {\n" +
  1121  					"  optional string fooBar = 1 [json_name=\"fooBar\"];\n" +
  1122  					"  optional string foo_bar = 2 [json_name=\"fooBar\"];\n" +
  1123  					"}\n",
  1124  			},
  1125  			"foo.proto:4:3: field Foo.foo_bar: custom JSON name \"fooBar\" conflicts with custom JSON name of field fooBar, defined at foo.proto:3:3",
  1126  		},
  1127  		{
  1128  			map[string]string{
  1129  				"foo.proto": "syntax = \"proto2\";\n" +
  1130  					"message Foo {\n" +
  1131  					"  optional string fooBar = 1;\n" +
  1132  					"  optional string FOO_BAR = 2;\n" +
  1133  					"}\n",
  1134  			},
  1135  			"", // should succeed: only check default JSON names in proto3
  1136  		},
  1137  		{
  1138  			map[string]string{
  1139  				"foo.proto": "syntax = \"proto2\";\n" +
  1140  					"message Foo {\n" +
  1141  					"  optional string fooBar = 1;\n" +
  1142  					"  optional string __foo_bar = 2;\n" +
  1143  					"}\n",
  1144  			},
  1145  			"", // should succeed: only check default JSON names in proto3
  1146  		},
  1147  		{
  1148  			map[string]string{
  1149  				"foo.proto": "syntax = \"proto3\";\n" +
  1150  					"enum Foo {\n" +
  1151  					"  true = 0;\n" +
  1152  					"  TRUE = 1;\n" +
  1153  					"}\n",
  1154  			},
  1155  			"foo.proto:4:3: enum value Foo.TRUE: camel-case name (with optional enum name prefix removed) \"True\" conflicts with camel-case name of enum value true, defined at foo.proto:3:3",
  1156  		},
  1157  		{
  1158  			map[string]string{
  1159  				"foo.proto": "syntax = \"proto3\";\n" +
  1160  					"message Blah {\n" +
  1161  					"  enum Foo {\n" +
  1162  					"    true = 0;\n" +
  1163  					"    TRUE = 1;\n" +
  1164  					"  }\n" +
  1165  					"}\n",
  1166  			},
  1167  			"foo.proto:5:5: enum value Foo.TRUE: camel-case name (with optional enum name prefix removed) \"True\" conflicts with camel-case name of enum value true, defined at foo.proto:4:5",
  1168  		}, {
  1169  			map[string]string{
  1170  				"foo.proto": "syntax = \"proto3\";\n" +
  1171  					"enum Foo {\n" +
  1172  					"  BAR_BAZ = 0;\n" +
  1173  					"  Foo_Bar_Baz = 1;\n" +
  1174  					"}\n",
  1175  			},
  1176  			"foo.proto:4:3: enum value Foo.Foo_Bar_Baz: camel-case name (with optional enum name prefix removed) \"BarBaz\" conflicts with camel-case name of enum value BAR_BAZ, defined at foo.proto:3:3",
  1177  		},
  1178  		{
  1179  			map[string]string{
  1180  				"foo.proto": "syntax = \"proto3\";\n" +
  1181  					"enum Foo {\n" +
  1182  					"  option allow_alias = true;\n" +
  1183  					"  BAR_BAZ = 0;\n" +
  1184  					"  FooBarBaz = 0;\n" +
  1185  					"}\n",
  1186  			},
  1187  			"", // should succeed: not a conflict if both values have same number
  1188  		},
  1189  	}
  1190  	for i, tc := range testCases {
  1191  		acc := func(filename string) (io.ReadCloser, error) {
  1192  			f, ok := tc.input[filename]
  1193  			if !ok {
  1194  				return nil, fmt.Errorf("file not found: %s", filename)
  1195  			}
  1196  			return io.NopCloser(strings.NewReader(f)), nil
  1197  		}
  1198  		names := make([]string, 0, len(tc.input))
  1199  		for k := range tc.input {
  1200  			names = append(names, k)
  1201  		}
  1202  		_, err := Parser{Accessor: acc}.ParseFiles(names...)
  1203  		if tc.errMsg == "" {
  1204  			if err != nil {
  1205  				t.Errorf("case %d: expecting no error; instead got error %q", i, err)
  1206  			}
  1207  		} else if err == nil {
  1208  			t.Errorf("case %d: expecting validation error %q; instead got no error", i, tc.errMsg)
  1209  		} else {
  1210  			options := strings.Split(tc.errMsg, "||")
  1211  			matched := false
  1212  			for i := range options {
  1213  				options[i] = strings.TrimSpace(options[i])
  1214  				if err.Error() == options[i] {
  1215  					matched = true
  1216  					break
  1217  				}
  1218  			}
  1219  			if !matched {
  1220  				t.Errorf("case %d: expecting validation error %q; instead got: %q", i, strings.Join(options, " || "), err)
  1221  			}
  1222  		}
  1223  	}
  1224  }
  1225  
  1226  func TestProto3Enums(t *testing.T) {
  1227  	file1 := `syntax = "<SYNTAX>"; enum bar { A = 0; B = 1; }`
  1228  	file2 := `syntax = "<SYNTAX>"; import "f1.proto"; message foo { <LABEL> bar bar = 1; }`
  1229  	getFileContents := func(file, syntax string) string {
  1230  		contents := strings.Replace(file, "<SYNTAX>", syntax, 1)
  1231  		label := ""
  1232  		if syntax == "proto2" {
  1233  			label = "optional"
  1234  		}
  1235  		return strings.Replace(contents, "<LABEL>", label, 1)
  1236  	}
  1237  
  1238  	syntaxOptions := []string{"proto2", "proto3"}
  1239  	for _, o1 := range syntaxOptions {
  1240  		fc1 := getFileContents(file1, o1)
  1241  
  1242  		for _, o2 := range syntaxOptions {
  1243  			fc2 := getFileContents(file2, o2)
  1244  
  1245  			// now parse the protos
  1246  			acc := func(filename string) (io.ReadCloser, error) {
  1247  				var data string
  1248  				switch filename {
  1249  				case "f1.proto":
  1250  					data = fc1
  1251  				case "f2.proto":
  1252  					data = fc2
  1253  				default:
  1254  					return nil, fmt.Errorf("file not found: %s", filename)
  1255  				}
  1256  				return io.NopCloser(strings.NewReader(data)), nil
  1257  			}
  1258  			_, err := Parser{Accessor: acc}.ParseFiles("f1.proto", "f2.proto")
  1259  
  1260  			if o1 != o2 && o2 == "proto3" {
  1261  				expected := "f2.proto:1:54: field foo.bar: cannot use proto2 enum bar in a proto3 message"
  1262  				if err == nil {
  1263  					t.Errorf("expecting validation error; instead got no error")
  1264  				} else if err.Error() != expected {
  1265  					t.Errorf("expecting validation error %q; instead got: %q", expected, err)
  1266  				}
  1267  			} else {
  1268  				// other cases succeed (okay to for proto2 to use enum from proto3 file and
  1269  				// obviously okay for proto2 importing proto2 and proto3 importing proto3)
  1270  				testutil.Ok(t, err)
  1271  			}
  1272  		}
  1273  	}
  1274  }
  1275  
  1276  func TestCustomErrorReporterWithLinker(t *testing.T) {
  1277  	input := map[string]string{
  1278  		"a/b/b.proto": `package a.b;
  1279  
  1280  import "google/protobuf/descriptor.proto";
  1281  
  1282  extend google.protobuf.FieldOptions {
  1283    optional Foo foo = 50001;
  1284  }
  1285  
  1286  message Foo {
  1287    optional string bar = 1;
  1288  }`,
  1289  		"a/c/c.proto": `import "a/b/b.proto";
  1290  
  1291  message ReferencesFooOption {
  1292    optional string baz = 1 [(a.b.foo).bat = "hello"];
  1293  }`,
  1294  	}
  1295  	errMsg := "a/c/c.proto:4:38: field ReferencesFooOption.baz: option (a.b.foo).bat: field bat of a.b.Foo does not exist"
  1296  
  1297  	acc := func(filename string) (io.ReadCloser, error) {
  1298  		f, ok := input[filename]
  1299  		if !ok {
  1300  			return nil, fmt.Errorf("file not found: %s", filename)
  1301  		}
  1302  		return io.NopCloser(strings.NewReader(f)), nil
  1303  	}
  1304  	names := make([]string, 0, len(input))
  1305  	for k := range input {
  1306  		names = append(names, k)
  1307  	}
  1308  	var errs []error
  1309  	_, err := Parser{
  1310  		Accessor: acc,
  1311  		ErrorReporter: func(errorWithPos ErrorWithPos) error {
  1312  			errs = append(errs, errorWithPos)
  1313  			// need to return nil to make sure this test case works
  1314  			// this will result in us only getting an error from errorHandler.getError()
  1315  			// we need to make sure this is called correctly in the linker so that all
  1316  			// errors are properly propagated from the return value of linkFiles(), and
  1317  			// therefor Parse returns ErrInvalidSource
  1318  			return nil
  1319  		},
  1320  	}.ParseFiles(names...)
  1321  	if err != ErrInvalidSource {
  1322  		t.Errorf("expecting validation error %v; instead got: %v", ErrInvalidSource, err)
  1323  	} else if len(errs) != 1 || errs[0].Error() != errMsg {
  1324  		t.Errorf("expecting validation error %q; instead got: %q", errs[0].Error(), errMsg)
  1325  	}
  1326  }
  1327  
  1328  func TestSyntheticOneOfCollisions(t *testing.T) {
  1329  	input := map[string]string{
  1330  		"foo1.proto": "syntax = \"proto3\";\n" +
  1331  			"message Foo {\n" +
  1332  			"  optional string bar = 1;\n" +
  1333  			"}\n",
  1334  		"foo2.proto": "syntax = \"proto3\";\n" +
  1335  			"message Foo {\n" +
  1336  			"  optional string bar = 1;\n" +
  1337  			"}\n",
  1338  	}
  1339  	acc := func(filename string) (io.ReadCloser, error) {
  1340  		f, ok := input[filename]
  1341  		if !ok {
  1342  			return nil, fmt.Errorf("file not found: %s", filename)
  1343  		}
  1344  		return io.NopCloser(strings.NewReader(f)), nil
  1345  	}
  1346  
  1347  	var errs []error
  1348  	errReporter := func(errorWithPos ErrorWithPos) error {
  1349  		errs = append(errs, errorWithPos)
  1350  		// need to return nil to accumulate all errors so we can report synthetic
  1351  		// oneof collision; otherwise, the link will fail after the first collision
  1352  		// and we'll never test the synthetic oneofs
  1353  		return nil
  1354  	}
  1355  
  1356  	_, err := Parser{
  1357  		Accessor:      acc,
  1358  		ErrorReporter: errReporter,
  1359  	}.ParseFiles("foo1.proto", "foo2.proto")
  1360  
  1361  	testutil.Eq(t, ErrInvalidSource, err)
  1362  	expectedOption1 := []string{
  1363  		`foo1.proto:2:9: symbol "Foo" already defined at foo2.proto:2:9`,
  1364  		`foo1.proto:3:19: symbol "Foo.bar" already defined at foo2.proto:3:19`,
  1365  		`foo1.proto:3:19: symbol "Foo._bar" already defined at foo2.proto:3:19`,
  1366  	}
  1367  	expectedOption2 := []string{
  1368  		`foo2.proto:2:9: symbol "Foo" already defined at foo1.proto:2:9`,
  1369  		`foo2.proto:3:19: symbol "Foo.bar" already defined at foo1.proto:3:19`,
  1370  		`foo2.proto:3:19: symbol "Foo._bar" already defined at foo1.proto:3:19`,
  1371  	}
  1372  
  1373  	var actual []string
  1374  	for _, err := range errs {
  1375  		actual = append(actual, err.Error())
  1376  	}
  1377  	// Errors expected depend on which file is compiled first. This is mostly deterministic
  1378  	// with parallelism of 1, but some things (like enabling -race in tests) can change the
  1379  	// expected order.
  1380  	if !reflect.DeepEqual(expectedOption1, actual) && !reflect.DeepEqual(expectedOption2, actual) {
  1381  		t.Errorf("got errors:\n:%v\nbut wanted EITHER:\n%v\n  OR:\n%v", actual, expectedOption1, expectedOption2)
  1382  	}
  1383  }
  1384  
  1385  func TestCustomJSONNameWarnings(t *testing.T) {
  1386  	testCases := []struct {
  1387  		source  string
  1388  		warning string
  1389  	}{
  1390  		{
  1391  			source: "syntax = \"proto2\";\n" +
  1392  				"message Foo {\n" +
  1393  				"  optional string foo_bar = 1;\n" +
  1394  				"  optional string fooBar = 2;\n" +
  1395  				"}\n",
  1396  			warning: "test.proto:4:3: field Foo.fooBar: default JSON name \"fooBar\" conflicts with default JSON name of field foo_bar, defined at test.proto:3:3",
  1397  		},
  1398  		{
  1399  			source: "syntax = \"proto2\";\n" +
  1400  				"message Foo {\n" +
  1401  				"  optional string foo_bar = 1;\n" +
  1402  				"  optional string fooBar = 2;\n" +
  1403  				"}\n",
  1404  			warning: "test.proto:4:3: field Foo.fooBar: default JSON name \"fooBar\" conflicts with default JSON name of field foo_bar, defined at test.proto:3:3",
  1405  		},
  1406  		// in nested message
  1407  		{
  1408  			source: "syntax = \"proto2\";\n" +
  1409  				"message Blah { message Foo {\n" +
  1410  				"  optional string foo_bar = 1;\n" +
  1411  				"  optional string fooBar = 2;\n" +
  1412  				"} }\n",
  1413  			warning: "test.proto:4:3: field Foo.fooBar: default JSON name \"fooBar\" conflicts with default JSON name of field foo_bar, defined at test.proto:3:3",
  1414  		},
  1415  		{
  1416  			source: "syntax = \"proto2\";\n" +
  1417  				"message Blah { message Foo {\n" +
  1418  				"  optional string foo_bar = 1;\n" +
  1419  				"  optional string fooBar = 2;\n" +
  1420  				"} }\n",
  1421  			warning: "test.proto:4:3: field Foo.fooBar: default JSON name \"fooBar\" conflicts with default JSON name of field foo_bar, defined at test.proto:3:3",
  1422  		},
  1423  		// enum values
  1424  		{
  1425  			source: "syntax = \"proto2\";\n" +
  1426  				"enum Foo {\n" +
  1427  				"  true = 0;\n" +
  1428  				"  TRUE = 1;\n" +
  1429  				"}\n",
  1430  			warning: "test.proto:4:3: enum value Foo.TRUE: camel-case name (with optional enum name prefix removed) \"True\" conflicts with camel-case name of enum value true, defined at test.proto:3:3",
  1431  		},
  1432  		{
  1433  			source: "syntax = \"proto2\";\n" +
  1434  				"enum Foo {\n" +
  1435  				"  fooBar_Baz = 0;\n" +
  1436  				"  _FOO__BAR_BAZ = 1;\n" +
  1437  				"}\n",
  1438  			warning: "test.proto:4:3: enum value Foo._FOO__BAR_BAZ: camel-case name (with optional enum name prefix removed) \"BarBaz\" conflicts with camel-case name of enum value fooBar_Baz, defined at test.proto:3:3",
  1439  		},
  1440  		{
  1441  			source: "syntax = \"proto2\";\n" +
  1442  				"enum Foo {\n" +
  1443  				"  fooBar_Baz = 0;\n" +
  1444  				"  FOO__BAR__BAZ__ = 1;\n" +
  1445  				"}\n",
  1446  			warning: "test.proto:4:3: enum value Foo.FOO__BAR__BAZ__: camel-case name (with optional enum name prefix removed) \"BarBaz\" conflicts with camel-case name of enum value fooBar_Baz, defined at test.proto:3:3",
  1447  		},
  1448  		{
  1449  			source: "syntax = \"proto2\";\n" +
  1450  				"enum Foo {\n" +
  1451  				"  fooBarBaz = 0;\n" +
  1452  				"  _FOO__BAR_BAZ = 1;\n" +
  1453  				"}\n",
  1454  			warning: "",
  1455  		},
  1456  		{
  1457  			source: "syntax = \"proto2\";\n" +
  1458  				"enum Foo {\n" +
  1459  				"  option allow_alias = true;\n" +
  1460  				"  Bar_Baz = 0;\n" +
  1461  				"  _BAR_BAZ_ = 0;\n" +
  1462  				"  FOO_BAR_BAZ = 0;\n" +
  1463  				"  foobar_baz = 0;\n" +
  1464  				"}\n",
  1465  			warning: "",
  1466  		},
  1467  		// in nested message
  1468  		{
  1469  			source: "syntax = \"proto2\";\n" +
  1470  				"message Blah { enum Foo {\n" +
  1471  				"  true = 0;\n" +
  1472  				"  TRUE = 1;\n" +
  1473  				"} }\n",
  1474  			warning: "test.proto:4:3: enum value Foo.TRUE: camel-case name (with optional enum name prefix removed) \"True\" conflicts with camel-case name of enum value true, defined at test.proto:3:3",
  1475  		},
  1476  		{
  1477  			source: "syntax = \"proto2\";\n" +
  1478  				"message Blah { enum Foo {\n" +
  1479  				"  fooBar_Baz = 0;\n" +
  1480  				"  _FOO__BAR_BAZ = 1;\n" +
  1481  				"} }\n",
  1482  			warning: "test.proto:4:3: enum value Foo._FOO__BAR_BAZ: camel-case name (with optional enum name prefix removed) \"BarBaz\" conflicts with camel-case name of enum value fooBar_Baz, defined at test.proto:3:3",
  1483  		},
  1484  		{
  1485  			source: "syntax = \"proto2\";\n" +
  1486  				"message Blah { enum Foo {\n" +
  1487  				"  option allow_alias = true;\n" +
  1488  				"  Bar_Baz = 0;\n" +
  1489  				"  _BAR_BAZ_ = 0;\n" +
  1490  				"  FOO_BAR_BAZ = 0;\n" +
  1491  				"  foobar_baz = 0;\n" +
  1492  				"} }\n",
  1493  			warning: "",
  1494  		},
  1495  	}
  1496  	for i, tc := range testCases {
  1497  		acc := func(filename string) (io.ReadCloser, error) {
  1498  			if filename == "test.proto" {
  1499  				return io.NopCloser(strings.NewReader(tc.source)), nil
  1500  			}
  1501  			return nil, fmt.Errorf("file not found: %s", filename)
  1502  		}
  1503  		var warnings []string
  1504  		warnFunc := func(err ErrorWithPos) {
  1505  			warnings = append(warnings, err.Error())
  1506  		}
  1507  		_, err := Parser{Accessor: acc, WarningReporter: warnFunc}.ParseFiles("test.proto")
  1508  		if err != nil {
  1509  			t.Errorf("case %d: expecting no error; instead got error %q", i, err)
  1510  		}
  1511  		if tc.warning == "" && len(warnings) > 0 {
  1512  			t.Errorf("case %d: expecting no warnings; instead got: %v", i, warnings)
  1513  		} else if tc.warning != "" {
  1514  			found := false
  1515  			for _, w := range warnings {
  1516  				if w == tc.warning {
  1517  					found = true
  1518  					break
  1519  				}
  1520  			}
  1521  			if !found {
  1522  				t.Errorf("case %d: expecting warning %q; instead got: %v", i, tc.warning, warnings)
  1523  			}
  1524  		}
  1525  	}
  1526  }